(comint-filter): Increment opoint only if after insertion point.
[bpt/emacs.git] / lisp / vc.el
CommitLineData
594722a8
ER
1;;; vc.el --- drive a version-control system from within Emacs
2
3;; Copyright (C) 1992 Free Software Foundation, Inc.
4
5;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
e1f297e6 6;; Version: 5.4
594722a8
ER
7
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation; either version 2, or (at your option)
13;; any later version.
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
21;; along with GNU Emacs; see the file COPYING. If not, write to
22;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23
24;;; Commentary:
25
1a2f456b
ER
26;; This mode is fully documented in the Emacs user's manual.
27;;
594722a8
ER
28;; This was designed and implemented by Eric Raymond <esr@snark.thyrsus.com>.
29;; Paul Eggert <eggert@twinsun.com>, Sebastian Kremer <sk@thp.uni-koeln.de>,
30;; and Richard Stallman contributed valuable criticism, support, and testing.
31;;
32;; Supported version-control systems presently include SCCS and RCS;
33;; your RCS version should be 5.6.2 or later for proper operation of
34;; the lock-breaking code.
35;;
36;; The RCS code assumes strict locking. You can support the RCS -x option
37;; by adding pairs to the vc-master-templates list.
38;;
39;; Proper function of the SCCS diff commands requires the shellscript vcdiff
40;; to be installed somewhere on Emacs's path for executables.
41;;
1a2f456b 42;; If your site uses the ChangeLog convention supported by Emacs, the
594be62e 43;; function vc-comment-to-change-log should prove a useful checkin hook.
1a2f456b 44;;
594722a8 45;; This code depends on call-process passing back the subprocess exit
e1f297e6 46;; status. Thus, you need Emacs 18.58 or later to run it. For the
6ed5075c 47;; vc-directory command to work properly as documented, you need 19.
7ef84cf9 48;; You also need Emacs 19's ring.el.
594722a8
ER
49;;
50;; The vc code maintains some internal state in order to reduce expensive
51;; version-control operations to a minimum. Some names are only computed
52;; once. If you perform version control operations with RCS/SCCS/CVS while
53;; vc's back is turned, or move/rename master files while vc is running,
54;; vc may get seriously confused. Don't do these things!
55;;
56;; Developer's notes on some concurrency issues are included at the end of
57;; the file.
58
59;;; Code:
60
61(require 'vc-hooks)
8c0aaf40 62(require 'ring)
e1f297e6 63(require 'dired)
8c0aaf40
ER
64(require 'compile)
65(require 'sendmail)
66
67(if (not (assoc 'vc-parent-buffer minor-mode-alist))
68 (setq minor-mode-alist
69 (cons '(vc-parent-buffer vc-parent-buffer-name)
70 minor-mode-alist)))
594722a8
ER
71
72;; General customization
73
74(defvar vc-default-back-end nil
75 "*Back-end actually used by this interface; may be SCCS or RCS.
76The value is only computed when needed to avoid an expensive search.")
594722a8
ER
77(defvar vc-suppress-confirm nil
78 "*If non-nil, reat user as expert; suppress yes-no prompts on some things.")
79(defvar vc-keep-workfiles t
80 "*If non-nil, don't delete working files after registering changes.")
81(defvar vc-initial-comment nil
82 "*Prompt for initial comment when a file is registered.")
83(defvar vc-command-messages nil
84 "*Display run messages from back-end commands.")
85(defvar vc-mistrust-permissions 'file-symlink-p
86 "*Don't assume that permissions and ownership track version-control status.")
666a0ebb
RM
87(defvar vc-checkin-switches nil
88 "*Extra switches passed to the checkin program by \\[vc-checkin].")
89
8c0aaf40
ER
90(defconst vc-maximum-comment-ring-size 32
91 "Maximum number of saved comments in the comment ring.")
92
2e810285
RS
93;;; This is duplicated in diff.el.
94(defvar diff-switches "-c"
95 "*A string or list of strings specifying switches to be be passed to diff.")
96
67242a23
RM
97;;;###autoload
98(defvar vc-checkin-hook nil
02da6253 99 "*List of functions called after a vc-checkin is done. See `run-hooks'.")
67242a23 100
594722a8
ER
101;; Header-insertion hair
102
103(defvar vc-header-alist
104 '((SCCS "\%W\%") (RCS "\$Id\$"))
105 "*Header keywords to be inserted when vc-insert-header is executed.")
106(defconst vc-static-header-alist
107 '(("\\.c$" .
108 "\n#ifndef lint\nstatic char vcid[] = \"\%s\";\n#endif /* lint */\n"))
109 "*Associate static header string templates with file types. A \%s in the
110template is replaced with the first string associated with the file's
7b4f934d
ER
111verson-control type in vc-header-alist.")
112
594722a8
ER
113(defvar vc-comment-alist
114 '((nroff-mode ".\\\"" ""))
115 "*Special comment delimiters to be used in generating vc headers only.
116Add an entry in this list if you need to override the normal comment-start
117and comment-end variables. This will only be necessary if the mode language
118is sensitive to blank lines.")
119
120;; Variables the user doesn't need to know about.
121(defvar vc-log-entry-mode nil)
122(defvar vc-log-operation nil)
67242a23 123(defvar vc-log-after-operation-hook nil)
02da6253 124(defvar vc-checkout-writeable-buffer-hook 'vc-checkout-writeable-buffer)
1a2f456b 125(defvar vc-parent-buffer nil)
8c0aaf40 126(defvar vc-parent-buffer-name nil)
594722a8 127
db59472c
RS
128(defvar vc-log-file)
129(defvar vc-log-version)
130
594722a8
ER
131(defconst vc-name-assoc-file "VC-names")
132
8c0aaf40 133(defvar vc-dired-mode nil)
e1f297e6
ER
134(make-variable-buffer-local 'vc-dired-mode)
135
8c0aaf40
ER
136(defvar vc-comment-ring nil)
137(defvar vc-comment-ring-index nil)
138(defvar vc-last-comment-match nil)
139
594722a8
ER
140;; File property caching
141
142(defun vc-file-clearprops (file)
143 ;; clear all properties of a given file
144 (setplist (intern file vc-file-prop-obarray) nil))
145
8c0aaf40
ER
146(defun vc-clear-context ()
147 "Clear all cached file properties and the comment ring."
148 (interactive)
149 (fillarray vc-file-prop-obarray nil)
150 ;; Note: there is potential for minor lossage here if there is an open
151 ;; log buffer with a nonzero local value of vc-comment-ring-index.
152 (setq vc-comment-ring nil))
153
594722a8
ER
154;; Random helper functions
155
156(defun vc-name (file)
157 "Return the master name of a file, nil if it is not registered"
158 (or (vc-file-getprop file 'vc-name)
159 (vc-file-setprop file 'vc-name
160 (let ((name-and-type (vc-registered file)))
161 (and name-and-type (car name-and-type))))))
162
7ef84cf9
RS
163(defun vc-registration-error (file)
164 (if file
165 (error "File %s is not under version control." file)
166 (error "Buffer %s is not associated with a file." (buffer-name))))
167
594722a8
ER
168(defvar vc-binary-assoc nil)
169
170(defun vc-find-binary (name)
171 "Look for a command anywhere on the subprocess-command search path."
172 (or (cdr (assoc name vc-binary-assoc))
173 (let ((full nil))
174 (catch 'found
175 (mapcar
176 (function (lambda (s)
177 (if (and s (file-exists-p (setq full (concat s "/" name))))
178 (throw 'found nil))))
179 exec-path))
180 (if full
181 (setq vc-binary-assoc (cons (cons name full) vc-binary-assoc)))
182 full)))
183
184(defun vc-do-command (okstatus command file &rest flags)
185 "Execute a version-control command, notifying user and checking for errors.
186The command is successful if its exit status does not exceed OKSTATUS.
187Output from COMMAND goes to buffer *vc*. The last argument of the command is
188the master name of FILE; this is appended to an optional list of FLAGS."
189 (setq file (expand-file-name file))
190 (if vc-command-messages
02da6253 191 (message "Running %s on %s..." command file))
1a2f456b 192 (let ((obuf (current-buffer)) (camefrom (current-buffer))
594722a8
ER
193 (squeezed nil)
194 (vc-file (and file (vc-name file)))
195 status)
196 (set-buffer (get-buffer-create "*vc*"))
8c0aaf40
ER
197 (set (make-local-variable 'vc-parent-buffer) camefrom)
198 (set (make-local-variable 'vc-parent-buffer-name)
199 (concat " from " (buffer-name camefrom)))
200
594722a8 201 (erase-buffer)
315e49ed
JB
202
203 ;; This is so that command arguments typed in the *vc* buffer will
204 ;; have reasonable defaults.
205 (setq default-directory (file-name-directory file))
206
594722a8
ER
207 (mapcar
208 (function (lambda (s) (and s (setq squeezed (append squeezed (list s))))))
209 flags)
210 (if vc-file
211 (setq squeezed (append squeezed (list vc-file))))
1a2f456b
ER
212 (let ((default-directory (file-name-directory (or file "./"))))
213 (setq status (apply 'call-process command nil t nil squeezed)))
594722a8
ER
214 (goto-char (point-max))
215 (previous-line 1)
216 (if (or (not (integerp status)) (< okstatus status))
217 (progn
218 (previous-line 1)
219 (print (cons command squeezed))
220 (next-line 1)
221 (pop-to-buffer "*vc*")
222 (vc-shrink-to-fit)
223 (goto-char (point-min))
02da6253
PE
224 (error "Running %s...FAILED (%s)" command
225 (if (integerp status)
226 (format "status %d" status)
227 status))
594722a8
ER
228 )
229 (if vc-command-messages
02da6253 230 (message "Running %s...OK" command))
594722a8
ER
231 )
232 (set-buffer obuf)
233 status)
234 )
235
c4ae7096
JB
236;;; Save a bit of the text around POSN in the current buffer, to help
237;;; us find the corresponding position again later. This works even
238;;; if all markers are destroyed or corrupted.
239(defun vc-position-context (posn)
240 (list posn
241 (buffer-size)
242 (buffer-substring posn
243 (min (point-max) (+ posn 100)))))
244
245;;; Return the position of CONTEXT in the current buffer, or nil if we
246;;; couldn't find it.
247(defun vc-find-position-by-context (context)
248 (let ((context-string (nth 2 context)))
249 (if (equal "" context-string)
250 (point-max)
251 (save-excursion
252 (let ((diff (- (nth 1 context) (buffer-size))))
253 (if (< diff 0) (setq diff (- diff)))
254 (goto-char (nth 0 context))
255 (if (or (search-forward context-string nil t)
256 ;; Can't use search-backward since the match may continue
257 ;; after point.
258 (progn (goto-char (- (point) diff (length context-string)))
259 ;; goto-char doesn't signal an error at
260 ;; beginning of buffer like backward-char would
261 (search-forward context-string nil t)))
262 ;; to beginning of OSTRING
263 (- (point) (length context-string))))))))
264
594722a8 265(defun vc-revert-buffer1 (&optional arg no-confirm)
7b4f934d 266 ;; Most of this was shamelessly lifted from Sebastian Kremer's rcs.el mode.
c4ae7096 267 ;; Revert buffer, try to keep point and mark where user expects them in spite
594722a8
ER
268 ;; of changes because of expanded version-control key words.
269 ;; This is quite important since otherwise typeahead won't work as expected.
270 (interactive "P")
271 (widen)
c4ae7096 272 (let ((point-context (vc-position-context (point)))
cfadef63
RS
273 ;; Use mark-marker to avoid confusion in transient-mark-mode.
274 (mark-context (if (eq (marker-buffer (mark-marker)) (current-buffer))
275 (vc-position-context (mark-marker))))
276 ;; Make the right thing happen in transient-mark-mode.
ab877583
RM
277 (mark-active nil)
278 ;; We may want to reparse the compilation buffer after revert
279 (reparse (and (boundp 'compilation-error-list) ;compile loaded
280 (let ((curbuf (current-buffer)))
281 ;; Construct a list; each elt is nil or a buffer
282 ;; iff that buffer is a compilation output buffer
283 ;; that contains markers into the current buffer.
284 (save-excursion
7ef84cf9
RS
285 (mapcar (function
286 (lambda (buffer)
ab877583
RM
287 (set-buffer buffer)
288 (let ((errors (or
289 compilation-old-error-list
290 compilation-error-list))
291 (buffer-error-marked-p nil))
292 (while (and errors
293 (not buffer-error-marked-p))
a1bda481 294 (and (markerp (cdr (car errors)))
e9c8e248
RM
295 (eq buffer
296 (marker-buffer
a1bda481 297 (cdr (car errors))))
e9c8e248 298 (setq buffer-error-marked-p t))
ab877583 299 (setq errors (cdr errors)))
7ef84cf9 300 (if buffer-error-marked-p buffer))))
ab877583 301 (buffer-list)))))))
7b4f934d
ER
302
303 ;; the actual revisit
594722a8 304 (revert-buffer arg no-confirm)
7b4f934d 305
ab877583
RM
306 ;; Reparse affected compilation buffers.
307 (while reparse
308 (if (car reparse)
309 (save-excursion
310 (set-buffer (car reparse))
311 (let ((compilation-last-buffer (current-buffer)) ;select buffer
312 ;; Record the position in the compilation buffer of
313 ;; the last error next-error went to.
314 (error-pos (marker-position
315 (car (car-safe compilation-error-list)))))
316 ;; Reparse the error messages as far as they were parsed before.
317 (compile-reinitialize-errors '(4) compilation-parsing-end)
318 ;; Move the pointer up to find the error we were at before
319 ;; reparsing. Now next-error should properly go to the next one.
320 (while (and compilation-error-list
27f2f10b 321 (/= error-pos (car (car compilation-error-list))))
ab877583
RM
322 (setq compilation-error-list (cdr compilation-error-list))))))
323 (setq reparse (cdr reparse)))
e1f297e6 324
7b4f934d 325 ;; Restore point and mark
c4ae7096
JB
326 (let ((new-point (vc-find-position-by-context point-context)))
327 (if new-point (goto-char new-point)))
328 (if mark-context
329 (let ((new-mark (vc-find-position-by-context mark-context)))
330 (if new-mark (set-mark new-mark))))))
331
594722a8
ER
332
333(defun vc-buffer-sync ()
334 ;; Make sure the current buffer and its working file are in sync
335 (if (and (buffer-modified-p)
336 (or
337 vc-suppress-confirm
338 (y-or-n-p (format "%s has been modified. Write it out? "
339 (buffer-name)))))
340 (save-buffer)))
341
342(defun vc-workfile-unchanged-p (file)
343 ;; Has the given workfile changed since last checkout?
344 (let ((checkout-time (vc-file-getprop file 'vc-checkout-time))
345 (lastmod (nth 5 (file-attributes file))))
346 (if checkout-time
347 (equal lastmod checkout-time)
348 (if (zerop (vc-backend-diff file nil))
349 (progn
350 (vc-file-setprop file 'vc-checkout-time lastmod)
351 t)
352 (progn
353 (vc-file-setprop file 'vc-checkout-time '(0 . 0))
354 nil
355 ))
356 )))
357
e1f297e6
ER
358(defun vc-next-action-on-file (file verbose &optional comment)
359 ;;; If comment is specified, it will be used as an admin or checkin comment.
360 (let (owner version (vc-file (vc-name file)))
361 (cond
362
363 ;; if there is no master file corresponding, create one
364 ((not vc-file)
365 (vc-register verbose comment)
366 (if vc-initial-comment
367 (setq vc-log-after-operation-hook
368 'vc-checkout-writeable-buffer-hook)
8c0aaf40 369 (vc-checkout-writeable-buffer file)))
e1f297e6
ER
370
371 ;; if there is no lock on the file, assert one and get it
372 ((not (setq owner (vc-locking-user file)))
8c0aaf40 373 (vc-checkout-writeable-buffer file))
e1f297e6
ER
374
375 ;; a checked-out version exists, but the user may not own the lock
376 ((not (string-equal owner (user-login-name)))
377 (if comment
378 (error "Sorry, you can't steal the lock on %s this way." file))
379 (vc-steal-lock
380 file
381 (and verbose (read-string "Version to steal: "))
382 owner))
383
384 ;; OK, user owns the lock on the file
8c0aaf40 385 (t
e1f297e6
ER
386 (find-file file)
387
388 ;; give luser a chance to save before checking in.
389 (vc-buffer-sync)
390
391 ;; Revert if file is unchanged and buffer is too.
392 ;; If buffer is modified, that means the user just said no
393 ;; to saving it; in that case, don't revert,
394 ;; because the user might intend to save
395 ;; after finishing the log entry.
396 (if (and (vc-workfile-unchanged-p file)
397 (not (buffer-modified-p)))
398 (progn
399 (vc-backend-revert file)
400 ;; DO NOT revert the file without asking the user!
401 (vc-resynch-window file t nil))
402
403 ;; user may want to set nonstandard parameters
404 (if verbose
405 (setq version (read-string "New version level: ")))
406
407 ;; OK, let's do the checkin
408 (vc-checkin file version comment)
8c0aaf40 409 )))))
e1f297e6
ER
410
411(defun vc-next-action-dired (file rev comment)
412 ;; We've accepted a log comment, now do a vc-next-action using it on all
413 ;; marked files.
414 (set-buffer vc-parent-buffer)
415 (dired-map-over-marks
416 (save-window-excursion
8c0aaf40
ER
417 (let ((file (dired-get-filename)))
418 (message "Processing %s..." file)
419 (vc-next-action-on-file file nil comment)
420 (message "Processing %s...done" file)))
421 nil t)
e1f297e6
ER
422 )
423
637a8ae9 424;; Here's the major entry point.
594722a8 425
637a8ae9 426;;;###autoload
594722a8
ER
427(defun vc-next-action (verbose)
428 "Do the next logical checkin or checkout operation on the current file.
429 If the file is not already registered, this registers it for version
430control and then retrieves a writeable, locked copy for editing.
431 If the file is registered and not locked by anyone, this checks out
432a writeable and locked file ready for editing.
433 If the file is checked out and locked by the calling user, this
434first checks to see if the file has changed since checkout. If not,
435it performs a revert.
e1f297e6
ER
436 If the file has been changed, this pops up a buffer for entry
437of a log message; when the message has been entered, it checks in the
594722a8
ER
438resulting changes along with the log message as change commentary. If
439the variable vc-keep-workfiles is non-nil (which is its default), a
440read-only copy of the changed file is left in place afterwards.
441 If the file is registered and locked by someone else, you are given
e1f297e6
ER
442the option to steal the lock.
443 If you call this from within a VC dired buffer with no files marked,
444it will operate on the file in the current line.
445 If you call this from within a VC dired buffer, and one or more
446files are marked, it will accept a log message and then operate on
447each one. The log message will be used as a comment for any register
448or checkin operations, but ignored when doing checkouts. Attempted
449lock steals will raise an error."
594722a8 450 (interactive "P")
8c0aaf40
ER
451 (catch 'nogo
452 (if vc-dired-mode
453 (let ((files (dired-get-marked-files)))
454 (if (= (length files) 1)
455 (find-file-other-window (dired-get-filename))
456 (vc-start-entry nil nil nil
457 "Enter a change comment for the marked files."
458 'vc-next-action-dired)
459 (throw 'nogo))))
e1f297e6 460 (while vc-parent-buffer
1a2f456b 461 (pop-to-buffer vc-parent-buffer))
e1f297e6
ER
462 (if buffer-file-name
463 (vc-next-action-on-file buffer-file-name verbose)
7ef84cf9 464 (vc-registration-error nil))))
594722a8
ER
465
466;;; These functions help the vc-next-action entry point
467
8c0aaf40 468(defun vc-checkout-writeable-buffer (&optional file)
02da6253 469 "Retrieve a writeable copy of the latest version of the current buffer's file."
8c0aaf40 470 (vc-checkout (or file (buffer-file-name)) t)
02da6253
PE
471 )
472
637a8ae9 473;;;###autoload
e1f297e6 474(defun vc-register (&optional override comment)
594722a8
ER
475 "Register the current file into your version-control system."
476 (interactive "P")
477 (if (vc-name buffer-file-name)
478 (error "This file is already registered."))
02da6253
PE
479 ;; Watch out for new buffers of size 0: the corresponding file
480 ;; does not exist yet, even though buffer-modified-p is nil.
481 (if (and (not (buffer-modified-p))
482 (zerop (buffer-size))
483 (not (file-exists-p buffer-file-name)))
484 (set-buffer-modified-p t))
594722a8
ER
485 (vc-buffer-sync)
486 (vc-admin
487 buffer-file-name
e1f297e6
ER
488 (and override
489 (read-string
490 (format "Initial version level for %s: " buffer-file-name))))
594722a8
ER
491 )
492
624b4662 493(defun vc-resynch-window (file &optional keep noquery)
594722a8
ER
494 ;; If the given file is in the current buffer,
495 ;; either revert on it so we see expanded keyworks,
496 ;; or unvisit it (depending on vc-keep-workfiles)
624b4662
RS
497 ;; NOQUERY if non-nil inhibits confirmation for reverting.
498 ;; NOQUERY should be t *only* if it is known the only difference
499 ;; between the buffer and the file is due to RCS rather than user editing!
594722a8
ER
500 (and (string= buffer-file-name file)
501 (if keep
502 (progn
1ab31687 503 (vc-revert-buffer1 t noquery)
594722a8
ER
504 (vc-mode-line buffer-file-name))
505 (progn
506 (delete-window)
507 (kill-buffer (current-buffer))))))
508
e1f297e6
ER
509(defun vc-start-entry (file rev comment msg action)
510 ;; Accept a comment for an operation on FILE revision REV. If COMMENT
511 ;; is nil, pop up a VC-log buffer, emit MSG, and set the
512 ;; action on close to ACTION; otherwise, do action immediately.
513 ;; Remember the file's buffer in parent-buffer (current one if no file).
514 (let ((parent (if file (find-file-noselect file) (current-buffer))))
515 (if comment
516 (set-buffer (get-buffer-create "*VC-log*"))
517 (pop-to-buffer (get-buffer-create "*VC-log*")))
8c0aaf40
ER
518 (set (make-local-variable 'vc-parent-buffer) parent)
519 (set (make-local-variable 'vc-parent-buffer-name)
520 (concat " from " (buffer-name vc-parent-buffer)))
e1f297e6
ER
521 (vc-mode-line (if file (file-name-nondirectory file) " (no file)"))
522 (vc-log-mode)
523 (setq vc-log-operation action)
524 (setq vc-log-file file)
525 (setq vc-log-version rev)
526 (if comment
527 (progn
528 (erase-buffer)
8c0aaf40
ER
529 (if (eq comment t)
530 (vc-finish-logentry t)
531 (insert comment)
532 (vc-finish-logentry nil)))
e1f297e6 533 (message "%s Type C-c C-c when done." msg))))
594722a8 534
e1f297e6 535(defun vc-admin (file rev &optional comment)
624b4662 536 "Check a file into your version-control system.
594722a8 537FILE is the unmodified name of the file. REV should be the base version
e1f297e6
ER
538level to check it in under. COMMENT, if specified, is the checkin comment."
539 (vc-start-entry file rev
540 (or comment (not vc-initial-comment))
541 "Enter initial comment." 'vc-backend-admin))
542
543(defun vc-checkout (file &optional writeable)
544 "Retrieve a copy of the latest version of the given file."
545 ;; If ftp is on this system and the name matches the ange-ftp format
546 ;; for a remote file, the user is trying something that won't work.
547 (if (and (string-match "^/[^/:]+:" file) (vc-find-binary "ftp"))
548 (error "Sorry, you can't check out files over FTP"))
549 (vc-backend-checkout file writeable)
550 (if (string-equal file buffer-file-name)
551 (vc-resynch-window file t t))
552 )
594722a8
ER
553
554(defun vc-steal-lock (file rev &optional owner)
555 "Steal the lock on the current workfile."
556 (interactive)
557 (if (not owner)
558 (setq owner (vc-locking-user file)))
559 (if (not (y-or-n-p (format "Take the lock on %s:%s from %s?" file rev owner)))
560 (error "Steal cancelled."))
ad014629
RS
561 (pop-to-buffer (get-buffer-create "*VC-mail*"))
562 (setq default-directory (expand-file-name "~/"))
563 (auto-save-mode auto-save-default)
564 (mail-mode)
565 (erase-buffer)
00b3f57b 566 (mail-setup owner (format "%s:%s" file rev) nil nil nil
ad014629
RS
567 (list (list 'vc-finish-steal file rev)))
568 (goto-char (point-max))
594722a8 569 (insert
ad014629 570 (format "I stole the lock on %s:%s, " file rev)
594722a8 571 (current-time-string)
ad014629
RS
572 ".\n")
573 (message "Please explain why you stole the lock. Type C-c C-c when done."))
594722a8 574
ad014629 575;; This is called when the notification has been sent.
594722a8 576(defun vc-finish-steal (file version)
594722a8 577 (vc-backend-steal file version)
624b4662 578 (vc-resynch-window file t t))
594722a8 579
594722a8
ER
580(defun vc-checkin (file &optional rev comment)
581 "Check in the file specified by FILE.
582The optional argument REV may be a string specifying the new version level
67242a23 583\(if nil increment the current level). The file is either retained with write
594722a8
ER
584permissions zeroed, or deleted (according to the value of vc-keep-workfiles).
585COMMENT is a comment string; if omitted, a buffer is
586popped up to accept a comment."
e1f297e6
ER
587 (setq vc-log-after-operation-hook 'vc-checkin-hook)
588 (vc-start-entry file rev comment "Enter a change comment." 'vc-backend-checkin))
594722a8 589
1a2f456b
ER
590;;; Here is a checkin hook that may prove useful to sites using the
591;;; ChangeLog facility supported by Emacs.
124250cf 592(defun vc-comment-to-change-log (&optional file)
8c0aaf40 593 "Update change log from VC change comments entered for the current file.
124250cf
ER
594Optional FILE specifies the change log file name; see `find-change-log'.
595See `vc-update-change-log'."
f495d400 596 (interactive)
124250cf 597 (let ((log (find-change-log file)))
1a2f456b
ER
598 (if log
599 (let ((default-directory (or (file-name-directory log)
600 default-directory)))
601 (vc-update-change-log
602 (file-relative-name buffer-file-name))))))
603
8c0aaf40 604(defun vc-finish-logentry (&optional nocomment)
594722a8
ER
605 "Complete the operation implied by the current log entry."
606 (interactive)
8c0aaf40
ER
607 ;; Check and record the comment, if any.
608 (if (not nocomment)
609 (progn
610 (goto-char (point-max))
611 (if (not (bolp))
612 (newline))
613 ;; Comment too long?
614 (vc-backend-logentry-check vc-log-file)
615 ;; Record the comment in the comment ring
616 (if (null vc-comment-ring)
617 (setq vc-comment-ring (make-ring vc-maximum-comment-ring-size)))
618 (ring-insert vc-comment-ring (buffer-string))
619 ))
594722a8
ER
620 ;; OK, do it to it
621 (if vc-log-operation
e1f297e6
ER
622 (save-excursion
623 (funcall vc-log-operation
624 vc-log-file
625 vc-log-version
626 (buffer-string)))
594722a8
ER
627 (error "No log operation is pending."))
628 ;; Return to "parent" buffer of this checkin and remove checkin window
e1f297e6
ER
629 (pop-to-buffer vc-parent-buffer)
630 (vc-error-occurred
631 (delete-window (get-buffer-window "*VC-log*")))
632 (kill-buffer "*VC-log*")
594722a8 633 ;; Now make sure we see the expanded headers
e1f297e6
ER
634 (if buffer-file-name
635 (vc-resynch-window buffer-file-name vc-keep-workfiles t))
636 (run-hooks vc-log-after-operation-hook))
594722a8
ER
637
638;; Code for access to the comment ring
639
8c0aaf40
ER
640(defun vc-previous-comment (arg)
641 "Cycle backwards through comment history."
642 (interactive "*p")
643 (let ((len (ring-length vc-comment-ring)))
644 (cond ((<= len 0)
645 (message "Empty comment ring")
646 (ding))
647 (t
648 (erase-buffer)
649 ;; Initialize the index on the first use of this command
650 ;; so that the first M-p gets index 0, and the first M-n gets
651 ;; index -1.
652 (if (null vc-comment-ring-index)
653 (setq vc-comment-ring-index
654 (if (> arg 0) -1
655 (if (< arg 0) 1 0))))
656 (setq vc-comment-ring-index
657 (ring-mod (+ vc-comment-ring-index arg) len))
658 (message "%d" (1+ vc-comment-ring-index))
659 (insert (ring-ref vc-comment-ring vc-comment-ring-index))))))
660
661(defun vc-next-comment (arg)
662 "Cycle forwards through comment history."
663 (interactive "*p")
664 (vc-previous-comment (- arg)))
665
666(defun vc-comment-search-reverse (str)
667 "Searches backwards through comment history for substring match."
668 (interactive "sComment substring: ")
669 (if (string= str "")
670 (setq str vc-last-comment-match)
671 (setq vc-last-comment-match str))
672 (if (null vc-comment-ring-index)
673 (setq vc-comment-ring-index -1))
674 (let ((str (regexp-quote str))
675 (len (ring-length vc-comment-ring))
676 (n (1+ vc-comment-ring-index)))
677 (while (and (< n len) (not (string-match str (ring-ref vc-comment-ring n))))
678 (setq n (+ n 1)))
679 (cond ((< n len)
680 (vc-previous-comment (- n vc-comment-ring-index)))
681 (t (error "Not found")))))
682
683(defun vc-comment-search-forward (str)
684 "Searches forwards through comment history for substring match."
685 (interactive "sComment substring: ")
686 (if (string= str "")
687 (setq str vc-last-comment-match)
688 (setq vc-last-comment-match str))
689 (if (null vc-comment-ring-index)
690 (setq vc-comment-ring-index 0))
691 (let ((str (regexp-quote str))
692 (len (ring-length vc-comment-ring))
693 (n vc-comment-ring-index))
694 (while (and (>= n 0) (not (string-match str (ring-ref vc-comment-ring n))))
695 (setq n (- n 1)))
696 (cond ((>= n 0)
697 (vc-next-comment (- n vc-comment-ring-index)))
698 (t (error "Not found")))))
594722a8
ER
699
700;; Additional entry points for examining version histories
701
637a8ae9 702;;;###autoload
594722a8 703(defun vc-diff (historic)
e8ee1ccf
RS
704 "Display diffs between file versions.
705Normally this compares the current file and buffer with the most recent
706checked in version of that file. This uses no arguments.
707With a prefix argument, it reads the file name to use
708and two version designators specifying which versions to compare."
594722a8 709 (interactive "P")
e1f297e6
ER
710 (if vc-dired-mode
711 (set-buffer (find-file-noselect (dired-get-filename))))
8ac8c82f 712 (while vc-parent-buffer
1a2f456b 713 (pop-to-buffer vc-parent-buffer))
594722a8
ER
714 (if historic
715 (call-interactively 'vc-version-diff)
8c0aaf40
ER
716 (if (or (null buffer-file-name) (null (vc-name buffer-file-name)))
717 (error "There is no version-control master associated with this buffer."))
02da6253 718 (let ((file buffer-file-name)
594722a8 719 unchanged)
7ef84cf9
RS
720 (or (and file (vc-name file))
721 (vc-registration-error file))
594722a8
ER
722 (vc-buffer-sync)
723 (setq unchanged (vc-workfile-unchanged-p buffer-file-name))
724 (if unchanged
02da6253 725 (message "No changes to %s since latest version." file)
594722a8 726 (vc-backend-diff file nil)
8c0aaf40
ER
727 ;; Ideally, we'd like at this point to parse the diff so that
728 ;; the buffer effectively goes into compilation mode and we
729 ;; can visit the old and new change locations via next-error.
730 ;; Unfortunately, this is just too painful to do. The basic
731 ;; problem is that the `old' file doesn't exist to be
732 ;; visited. This plays hell with numerous assumptions in
733 ;; the diff.el and compile.el machinery.
734 (pop-to-buffer "*vc*")
1a2f456b 735 (vc-shrink-to-fit)
594722a8
ER
736 (goto-char (point-min))
737 )
738 (not unchanged)
739 )
740 )
741 )
742
743(defun vc-version-diff (file rel1 rel2)
744 "For FILE, report diffs between two stored versions REL1 and REL2 of it.
745If FILE is a directory, generate diffs between versions for all registered
746files in or below it."
3234e2a3 747 (interactive "FFile or directory to diff: \nsOlder version: \nsNewer version: ")
594722a8
ER
748 (if (string-equal rel1 "") (setq rel1 nil))
749 (if (string-equal rel2 "") (setq rel2 nil))
750 (if (file-directory-p file)
1a2f456b 751 (let ((camefrom (current-buffer)))
594722a8 752 (set-buffer (get-buffer-create "*vc-status*"))
8c0aaf40
ER
753 (set (make-local-variable 'vc-parent-buffer) camefrom)
754 (set (make-local-variable 'vc-parent-buffer-name)
755 (concat " from " (buffer-name camefrom)))
594722a8 756 (erase-buffer)
3234e2a3
ER
757 (insert "Diffs between "
758 (or rel1 "last version checked in")
759 " and "
760 (or rel2 "current workfile(s)")
761 ":\n\n")
594722a8 762 (set-buffer (get-buffer-create "*vc*"))
e1f297e6 763 (cd file)
594722a8
ER
764 (vc-file-tree-walk
765 (function (lambda (f)
a9a59766 766 (message "Looking at %s" f)
594722a8 767 (and
3234e2a3
ER
768 (not (file-directory-p f))
769 (vc-registered f)
02da6253
PE
770 (vc-backend-diff f rel1 rel2)
771 (append-to-buffer "*vc-status*" (point-min) (point-max)))
772 )))
594722a8
ER
773 (pop-to-buffer "*vc-status*")
774 (insert "\nEnd of diffs.\n")
775 (goto-char (point-min))
776 (set-buffer-modified-p nil)
777 )
778 (progn
779 (vc-backend-diff file rel1 rel2)
780 (goto-char (point-min))
781 (if (equal (point-min) (point-max))
02da6253 782 (message "No changes to %s between %s and %s." file rel1 rel2)
594722a8
ER
783 (pop-to-buffer "*vc*")
784 (goto-char (point-min))
785 )
786 )
787 )
788 )
789
790;; Header-insertion code
791
637a8ae9 792;;;###autoload
594722a8
ER
793(defun vc-insert-headers ()
794 "Insert headers in a file for use with your version-control system.
795Headers desired are inserted at the start of the buffer, and are pulled from
7b4f934d 796the variable vc-header-alist"
594722a8 797 (interactive)
e1f297e6
ER
798 (if vc-dired-mode
799 (find-file-other-window (dired-get-filename)))
8ac8c82f 800 (while vc-parent-buffer
1a2f456b 801 (pop-to-buffer vc-parent-buffer))
594722a8
ER
802 (save-excursion
803 (save-restriction
804 (widen)
805 (if (or (not (vc-check-headers))
806 (y-or-n-p "Version headers already exist. Insert another set?"))
807 (progn
808 (let* ((delims (cdr (assq major-mode vc-comment-alist)))
809 (comment-start-vc (or (car delims) comment-start "#"))
810 (comment-end-vc (or (car (cdr delims)) comment-end ""))
811 (hdstrings (cdr (assoc (vc-backend-deduce (buffer-file-name)) vc-header-alist))))
812 (mapcar (function (lambda (s)
813 (insert comment-start-vc "\t" s "\t"
814 comment-end-vc "\n")))
815 hdstrings)
816 (if vc-static-header-alist
817 (mapcar (function (lambda (f)
818 (if (string-match (car f) buffer-file-name)
819 (insert (format (cdr f) (car hdstrings))))))
820 vc-static-header-alist))
821 )
822 )))))
823
e1f297e6
ER
824;; The VC directory submode. Coopt Dired for this.
825;; All VC commands get mapped into logical equivalents.
826
ee7e9c88
RS
827(defvar vc-dired-prefix-map (make-sparse-keymap))
828(define-key vc-dired-prefix-map "\C-xv" vc-prefix-map)
829
1434b7aa
RS
830(or (not (boundp 'minor-mode-map-alist))
831 (assq 'vc-dired-mode minor-mode-map-alist)
e1f297e6 832 (setq minor-mode-map-alist
1434b7aa 833 (cons '(vc-dired-mode . vc-dired-prefix-map)
ee7e9c88 834 minor-mode-map-alist)))
e1f297e6
ER
835
836(defun vc-dired-mode ()
837 "The augmented Dired minor mode used in VC directory buffers.
838All Dired commands operate normally. Users currently locking listed files
ee7e9c88 839are listed in place of the file's owner and group.
e1f297e6
ER
840Keystrokes bound to VC commands will execute as though they had been called
841on a buffer attached to the file named in the current Dired buffer line."
842 (setq vc-dired-mode t)
843 (setq vc-mode " under VC"))
594722a8 844
8c0aaf40
ER
845(defun vc-dired-reformat-line (x)
846 ;; Hack a directory-listing line, plugging in locking-user info in
847 ;; place of the user and group info. Should have the beneficial
848 ;; side-effect of shortening the listing line. Each call starts with
849 ;; point immediately following the dired mark area on the line to be
850 ;; hacked.
851 ;;
852 ;; Simplest possible one:
853 ;; (insert (concat x "\t")))
854 ;;
855 ;; This code, like dired, assumes UNIX -l format.
856 (forward-word 1) ;; skip over any extra field due to -ibs options
857 (if x (setq x (concat "(" x ")")))
858 (if (re-search-forward "\\([0-9]+ \\).................\\( .*\\)" nil 0)
859 (let ((rep (substring (concat x " ") 0 9)))
860 (replace-match (concat "\\1" rep "\\2") t)))
861 )
862
6ed5075c
RS
863;;; Note in Emacs 18 the following defun gets overridden
864;;; with the symbol 'vc-directory-18. See below.
637a8ae9 865;;;###autoload
594722a8
ER
866(defun vc-directory (verbose)
867 "Show version-control status of all files under the current directory."
868 (interactive "P")
e1f297e6
ER
869 (let (nonempty
870 (dl (length default-directory))
871 (filelist nil) (userlist nil)
8c0aaf40
ER
872 dired-buf
873 dired-buf-mod-count)
e1f297e6
ER
874 (vc-file-tree-walk
875 (function (lambda (f)
876 (if (vc-registered f)
877 (let ((user (vc-locking-user f)))
878 (and (or verbose user)
879 (setq filelist (cons (substring f dl) filelist))
880 (setq userlist (cons user userlist))))))))
594722a8 881 (save-excursion
8c0aaf40
ER
882 ;; This uses a semi-documented featre of dired; giving a switch
883 ;; argument forces the buffer to refresh each time.
884 (dired
885 (cons default-directory (nreverse filelist))
886 dired-listing-switches)
887 (setq dired-buf (current-buffer))
888 (setq nonempty (not (zerop (buffer-size)))))
594722a8
ER
889 (if nonempty
890 (progn
e1f297e6
ER
891 (pop-to-buffer dired-buf)
892 (vc-dired-mode)
893 (goto-char (point-min))
894 (setq buffer-read-only nil)
8c0aaf40 895 (forward-line 1) ;; Skip header line
e1f297e6 896 (mapcar
7ef84cf9
RS
897 (function
898 (lambda (x)
8c0aaf40
ER
899 (forward-char 2) ;; skip dired's mark area
900 (vc-dired-reformat-line x)
7ef84cf9 901 (forward-line 1))) ;; go to next line
8c0aaf40 902 (nreverse userlist))
e1f297e6
ER
903 (setq buffer-read-only t)
904 (goto-char (point-min))
905 )
02da6253
PE
906 (message "No files are currently %s under %s"
907 (if verbose "registered" "locked") default-directory))
594722a8
ER
908 ))
909
6ed5075c
RS
910;; Emacs 18 version
911(defun vc-directory-18 (verbose)
912 "Show version-control status of all files under the current directory."
913 (interactive "P")
0ee7d623 914 (let (nonempty (dir default-directory))
6ed5075c
RS
915 (save-excursion
916 (set-buffer (get-buffer-create "*vc-status*"))
917 (erase-buffer)
0ee7d623 918 (cd dir)
6ed5075c
RS
919 (vc-file-tree-walk
920 (function (lambda (f)
921 (if (vc-registered f)
922 (let ((user (vc-locking-user f)))
923 (if (or user verbose)
924 (insert (format
925 "%s %s\n"
926 (concat user) f))))))))
927 (setq nonempty (not (zerop (buffer-size)))))
928 (if nonempty
929 (progn
930 (pop-to-buffer "*vc-status*" t)
931 (vc-shrink-to-fit)
932 (goto-char (point-min)))
933 (message "No files are currently %s under %s"
934 (if verbose "registered" "locked") default-directory))
935 ))
936
937(or (boundp 'minor-mode-map-alist)
938 (fset 'vc-directory 'vc-directory-18))
939
7ef84cf9
RS
940; Emacs 18 also lacks these.
941(or (boundp 'compilation-old-error-list)
942 (setq compilation-old-error-list nil))
943
594722a8
ER
944;; Named-configuration support for SCCS
945
946(defun vc-add-triple (name file rev)
947 (save-excursion
948 (find-file (concat (vc-backend-subdirectory-name file) "/" vc-name-assoc-file))
949 (goto-char (point-max))
950 (insert name "\t:\t" file "\t" rev "\n")
951 (basic-save-buffer)
952 (kill-buffer (current-buffer))
953 ))
954
955(defun vc-record-rename (file newname)
956 (save-excursion
957 (find-file (concat (vc-backend-subdirectory-name file) "/" vc-name-assoc-file))
958 (goto-char (point-min))
959 (replace-regexp (concat ":" (regexp-quote file) "$") (concat ":" newname))
960 (basic-save-buffer)
961 (kill-buffer (current-buffer))
962 ))
963
964(defun vc-lookup-triple (file name)
7b4f934d
ER
965 ;; Return the numeric version corresponding to a named snapshot of file
966 ;; If name is nil or a version number string it's just passed through
967 (cond ((null name) "")
968 ((let ((firstchar (aref name 0)))
969 (and (>= firstchar ?0) (<= firstchar ?9)))
970 name)
971 (t
972 (car (vc-master-info
973 (concat (vc-backend-subdirectory-name file) "/" vc-name-assoc-file)
974 (list (concat name "\t:\t" file "\t\\(.+\\)"))))
975 )))
594722a8
ER
976
977;; Named-configuration entry points
978
979(defun vc-quiescent-p ()
980 ;; Is the current directory ready to be snapshot?
02da6253
PE
981 (catch 'quiet
982 (vc-file-tree-walk
983 (function (lambda (f)
984 (if (and (vc-registered f) (vc-locking-user f))
985 (throw 'quiet nil)))))
986 t))
594722a8 987
637a8ae9 988;;;###autoload
594722a8
ER
989(defun vc-create-snapshot (name)
990 "Make a snapshot called NAME.
991The snapshot is made from all registered files at or below the current
992directory. For each file, the version level of its latest
993version becomes part of the named configuration."
994 (interactive "sNew snapshot name: ")
995 (if (not (vc-quiescent-p))
996 (error "Can't make a snapshot, locked files are in the way.")
997 (vc-file-tree-walk
998 (function (lambda (f) (and
594722a8 999 (vc-name f)
02da6253 1000 (vc-backend-assign-name f name)))))
594722a8
ER
1001 ))
1002
637a8ae9 1003;;;###autoload
594722a8
ER
1004(defun vc-retrieve-snapshot (name)
1005 "Retrieve the snapshot called NAME.
1006This function fails if any files are locked at or below the current directory
1007Otherwise, all registered files are checked out (unlocked) at their version
1008levels in the snapshot."
1009 (interactive "sSnapshot name to retrieve: ")
1010 (if (not (vc-quiescent-p))
1011 (error "Can't retrieve a snapshot, locked files are in the way.")
1012 (vc-file-tree-walk
1013 (function (lambda (f) (and
594722a8 1014 (vc-name f)
02da6253 1015 (vc-error-occurred (vc-backend-checkout f nil name))))))
594722a8
ER
1016 ))
1017
1018;; Miscellaneous other entry points
1019
637a8ae9 1020;;;###autoload
594722a8
ER
1021(defun vc-print-log ()
1022 "List the change log of the current buffer in a window."
1023 (interactive)
e1f297e6
ER
1024 (if vc-dired-mode
1025 (set-buffer (find-file-noselect (dired-get-filename))))
8ac8c82f 1026 (while vc-parent-buffer
1a2f456b 1027 (pop-to-buffer vc-parent-buffer))
594722a8
ER
1028 (if (and buffer-file-name (vc-name buffer-file-name))
1029 (progn
1030 (vc-backend-print-log buffer-file-name)
1031 (pop-to-buffer (get-buffer-create "*vc*"))
1a2f456b 1032 (vc-shrink-to-fit)
594722a8
ER
1033 (goto-char (point-min))
1034 )
7ef84cf9 1035 (vc-registration-error buffer-file-name)
594722a8
ER
1036 )
1037 )
1038
637a8ae9 1039;;;###autoload
594722a8 1040(defun vc-revert-buffer ()
9c95ac44
RS
1041 "Revert the current buffer's file back to the latest checked-in version.
1042This asks for confirmation if the buffer contents are not identical
1043to that version."
594722a8 1044 (interactive)
e1f297e6
ER
1045 (if vc-dired-mode
1046 (find-file-other-window (dired-get-filename)))
8ac8c82f 1047 (while vc-parent-buffer
1a2f456b 1048 (pop-to-buffer vc-parent-buffer))
594722a8
ER
1049 (let ((file buffer-file-name)
1050 (obuf (current-buffer)) (changed (vc-diff nil)))
9c95ac44
RS
1051 (if (and changed (or vc-suppress-confirm
1052 (not (yes-or-no-p "Discard changes? "))))
594722a8
ER
1053 (progn
1054 (delete-window)
1055 (error "Revert cancelled."))
1056 (set-buffer obuf))
1057 (if changed
1058 (delete-window))
1059 (vc-backend-revert file)
624b4662 1060 (vc-resynch-window file t t)
594722a8
ER
1061 )
1062 )
1063
637a8ae9 1064;;;###autoload
594722a8 1065(defun vc-cancel-version (norevert)
cdd8203f 1066 "Get rid of the version most recently checked in by anyone."
594722a8 1067 (interactive "P")
e1f297e6
ER
1068 (if vc-dired-mode
1069 (find-file-other-window (dired-get-filename)))
8ac8c82f
ER
1070 (while vc-parent-buffer
1071 (pop-to-buffer vc-parent-buffer))
1072 (let* ((target (concat (vc-latest-version (buffer-file-name))))
1073 (yours (concat (vc-your-latest-version (buffer-file-name))))
7b4f934d
ER
1074 (prompt (if (string-equal yours target)
1075 "Remove your version %s from master?"
1076 "Version %s was not your change. Remove it anyway?")))
1077 (if (null (yes-or-no-p (format prompt target)))
1078 nil
1079 (vc-backend-uncheck (buffer-file-name) target)
1080 (if norevert
1081 (vc-mode-line (buffer-file-name))
1082 (vc-checkout (buffer-file-name) nil)))
8ac8c82f 1083 ))
594722a8
ER
1084
1085(defun vc-rename-file (old new)
1086 "Rename a file, taking its master files with it."
1087 (interactive "fOld name: \nFNew name: ")
1088 (let ((oldbuf (get-file-buffer old)))
1089 (if (buffer-modified-p oldbuf)
1090 (error "Please save files before moving them."))
1091 (if (get-file-buffer new)
1092 (error "Already editing new file name."))
1093 (let ((oldmaster (vc-name old)))
1094 (if oldmaster
1095 (if (vc-locking-user old)
1096 (error "Please check in files before moving them."))
1097 (if (or (file-symlink-p oldmaster)
60213ed0
RS
1098 ;; This had FILE, I changed it to OLD. -- rms.
1099 (file-symlink-p (vc-backend-subdirectory-name old)))
594722a8
ER
1100 (error "This is not a safe thing to do in the presence of symbolic links."))
1101 (rename-file oldmaster (vc-name new)))
1102 (if (or (not oldmaster) (file-exists-p old))
1103 (rename-file old new)))
1104; ?? Renaming a file might change its contents due to keyword expansion.
1105; We should really check out a new copy if the old copy was precisely equal
1106; to some checked in version. However, testing for this is tricky....
1107 (if oldbuf
1108 (save-excursion
1109 (set-buffer oldbuf)
1110 (set-visited-file-name new)
1111 (set-buffer-modified-p nil))))
60213ed0
RS
1112 ;; This had FILE, I changed it to OLD. -- rms.
1113 (vc-backend-dispatch old
594722a8
ER
1114 (vc-record-rename old new)
1115 nil)
1116 )
1117
637a8ae9 1118;;;###autoload
f35ecf88
RM
1119(defun vc-update-change-log (&rest args)
1120 "Find change log file and add entries from recent RCS logs.
1121The mark is left at the end of the text prepended to the change log.
1122With prefix arg of C-u, only find log entries for the current buffer's file.
1123With any numeric prefix arg, find log entries for all files currently visited.
1124From a program, any arguments are passed to the `rcs2log' script."
67242a23
RM
1125 (interactive
1126 (cond ((consp current-prefix-arg) ;C-u
1127 (list buffer-file-name))
1128 (current-prefix-arg ;Numeric argument.
1129 (let ((files nil)
1130 (buffers (buffer-list))
1131 file)
1132 (while buffers
1133 (setq file (buffer-file-name (car buffers)))
1134 (and file (vc-backend-deduce file)
f480bf4b 1135 (setq files (cons (file-relative-name file) files)))
67242a23
RM
1136 (setq buffers (cdr buffers)))
1137 files))))
f35ecf88 1138 (find-file-other-window "ChangeLog")
02da6253 1139 (barf-if-buffer-read-only)
f35ecf88
RM
1140 (vc-buffer-sync)
1141 (undo-boundary)
1142 (goto-char (point-min))
02da6253 1143 (push-mark)
f35ecf88 1144 (message "Computing change log entries...")
e1f297e6 1145 (message "Computing change log entries... %s"
02da6253
PE
1146 (if (eq 0 (apply 'call-process "rcs2log" nil t nil args))
1147 "done" "failed")))
594722a8
ER
1148
1149;; Functions for querying the master and lock files.
1150
caf15d4f 1151(defun vc-match-substring (bn)
594722a8
ER
1152 (buffer-substring (match-beginning bn) (match-end bn)))
1153
1154(defun vc-parse-buffer (patterns &optional file properties)
1155 ;; Use PATTERNS to parse information out of the current buffer
1156 ;; by matching each regular expression in the list and returning \\1.
1157 ;; If a regexp has two tag brackets, assume the second is a date
1158 ;; field and we want the most recent entry matching the template.
1159 ;; If FILE and PROPERTIES are given, the latter must be a list of
1160 ;; properties of the same length as PATTERNS; each property is assigned
1161 ;; the corresponding value.
1162 (mapcar (function (lambda (p)
1163 (goto-char (point-min))
1164 (if (string-match "\\\\(.*\\\\(" p)
1165 (let ((latest-date "") (latest-val))
1166 (while (re-search-forward p nil t)
caf15d4f 1167 (let ((date (vc-match-substring 2)))
594722a8
ER
1168 (if (string< latest-date date)
1169 (progn
1170 (setq latest-date date)
1171 (setq latest-val
caf15d4f 1172 (vc-match-substring 1))))))
594722a8
ER
1173 latest-val))
1174 (prog1
1175 (and (re-search-forward p nil t)
caf15d4f 1176 (let ((value (vc-match-substring 1)))
594722a8
ER
1177 (if file
1178 (vc-file-setprop file (car properties) value))
1179 value))
1180 (setq properties (cdr properties)))))
1181 patterns)
1182 )
1183
1184(defun vc-master-info (file fields &optional rfile properties)
1185 ;; Search for information in a master file.
1186 (if (and file (file-exists-p file))
1187 (save-excursion
1188 (let ((buf))
1189 (setq buf (create-file-buffer file))
1190 (set-buffer buf))
1191 (erase-buffer)
1192 (insert-file-contents file nil)
1193 (set-buffer-modified-p nil)
1194 (auto-save-mode nil)
1195 (prog1
1196 (vc-parse-buffer fields rfile properties)
1197 (kill-buffer (current-buffer)))
1198 )
1199 (if rfile
1200 (mapcar
1201 (function (lambda (p) (vc-file-setprop rfile p nil)))
1202 properties))
1203 )
1204 )
1205
1206(defun vc-log-info (command file patterns &optional properties)
1207 ;; Search for information in log program output
1208 (if (and file (file-exists-p file))
1209 (save-excursion
1210 (let ((buf))
1211 (setq buf (get-buffer-create "*vc*"))
1212 (set-buffer buf))
1213 (apply 'vc-do-command 0 command file nil)
1214 (set-buffer-modified-p nil)
1215 (prog1
1216 (vc-parse-buffer patterns file properties)
1217 (kill-buffer (current-buffer))
1218 )
1219 )
1220 (if file
1221 (mapcar
1222 (function (lambda (p) (vc-file-setprop file p nil)))
1223 properties))
1224 )
1225 )
1226
1227(defun vc-locking-user (file)
1228 "Return the name of the person currently holding a lock on FILE.
1229Return nil if there is no such person."
e1f297e6 1230 (setq file (expand-file-name file)) ;; ??? Work around bug in 19.0.4
594722a8
ER
1231 (if (or (not vc-keep-workfiles)
1232 (eq vc-mistrust-permissions 't)
1233 (and vc-mistrust-permissions
1234 (funcall vc-mistrust-permissions (vc-backend-subdirectory-name file))))
1235 (vc-true-locking-user file)
1236 ;; This implementation assumes that any file which is under version
1237 ;; control and has -rw-r--r-- is locked by its owner. This is true
1238 ;; for both RCS and SCCS, which keep unlocked files at -r--r--r--.
1239 ;; We have to be careful not to exclude files with execute bits on;
1240 ;; scripts can be under version control too. The advantage of this
1241 ;; hack is that calls to the very expensive vc-fetch-properties
1242 ;; function only have to be made if (a) the file is locked by someone
1243 ;; other than the current user, or (b) some untoward manipulation
e1f297e6
ER
1244 ;; behind vc's back has changed the owner or the `group' or `other'
1245 ;; write bits.
594722a8
ER
1246 (let ((attributes (file-attributes file)))
1247 (cond ((string-match ".r-.r-.r-." (nth 8 attributes))
1248 nil)
1249 ((and (= (nth 2 attributes) (user-uid))
1250 (string-match ".rw.r-.r-." (nth 8 attributes)))
1251 (user-login-name))
1252 (t
1253 (vc-true-locking-user file))))))
1254
1255(defun vc-true-locking-user (file)
1256 ;; The slow but reliable version
1257 (vc-fetch-properties file)
1258 (vc-file-getprop file 'vc-locking-user))
1259
1260(defun vc-latest-version (file)
1261 ;; Return version level of the latest version of FILE
1262 (vc-fetch-properties file)
1263 (vc-file-getprop file 'vc-latest-version))
1264
1265(defun vc-your-latest-version (file)
1266 ;; Return version level of the latest version of FILE checked in by you
1267 (vc-fetch-properties file)
1268 (vc-file-getprop file 'vc-your-latest-version))
1269
1270;; Collect back-end-dependent stuff here
1271;;
1272;; Everything eventually funnels through these functions. To implement
1273;; support for a new version-control system, add another branch to the
7b4f934d
ER
1274;; vc-backend-dispatch macro and fill it in in each call. The variable
1275;; vc-master-templates in vc-hooks.el will also have to change.
594722a8
ER
1276
1277(defmacro vc-backend-dispatch (f s r)
1278 "Execute FORM1 or FORM2 depending on whether we're using SCCS or RCS."
1279 (list 'let (list (list 'type (list 'vc-backend-deduce f)))
1280 (list 'cond
1281 (list (list 'eq 'type (quote 'SCCS)) s) ;; SCCS
1282 (list (list 'eq 'type (quote 'RCS)) r) ;; RCS
1283 )))
1284
1285(defun vc-lock-file (file)
1286 ;; Generate lock file name corresponding to FILE
1287 (let ((master (vc-name file)))
1288 (and
1289 master
1290 (string-match "\\(.*/\\)s\\.\\(.*\\)" master)
1291 (concat
1292 (substring master (match-beginning 1) (match-end 1))
1293 "p."
1294 (substring master (match-beginning 2) (match-end 2))))))
1295
1296
1297(defun vc-fetch-properties (file)
1298 ;; Re-fetch all properties associated with the given file.
1299 ;; Currently these properties are:
1300 ;; vc-locking-user
1301 ;; vc-locked-version
1302 ;; vc-latest-version
1303 ;; vc-your-latest-version
1304 (vc-backend-dispatch
1305 file
1306 ;; SCCS
1307 (progn
1308 (vc-master-info (vc-lock-file file)
1309 (list
1310 "^[^ ]+ [^ ]+ \\([^ ]+\\)"
1311 "^\\([^ ]+\\)")
1312 file
1313 '(vc-locking-user vc-locked-version))
1314 (vc-master-info (vc-name file)
1315 (list
1316 "^\001d D \\([^ ]+\\)"
1317 (concat "^\001d D \\([^ ]+\\) .* "
1318 (regexp-quote (user-login-name)) " ")
1319 )
1320 file
1321 '(vc-latest-version vc-your-latest-version))
1322 )
1323 ;; RCS
1324 (vc-log-info "rlog" file
1325 (list
1326 "^locks: strict\n\t\\([^:]+\\)"
1327 "^locks: strict\n\t[^:]+: \\(.+\\)"
1328 "^revision[\t ]+\\([0-9.]+\\).*\ndate: \\([ /0-9:]+\\);"
1329 (concat
3234e2a3 1330 "^revision[\t ]+\\([0-9.]+\\)\n.*author: "
594722a8 1331 (regexp-quote (user-login-name))
3234e2a3 1332 ";"))
594722a8
ER
1333 '(vc-locking-user vc-locked-version
1334 vc-latest-version vc-your-latest-version))
1335 ))
1336
1337(defun vc-backend-subdirectory-name (&optional file)
1338 ;; Where the master and lock files for the current directory are kept
1339 (symbol-name
1340 (or
1341 (and file (vc-backend-deduce file))
1342 vc-default-back-end
1343 (setq vc-default-back-end (if (vc-find-binary "rcs") 'RCS 'SCCS)))))
1344
1345(defun vc-backend-admin (file &optional rev comment)
1346 ;; Register a file into the version-control system
1347 ;; Automatically retrieves a read-only version of the file with
1348 ;; keywords expanded if vc-keep-workfiles is non-nil, otherwise
1349 ;; it deletes the workfile.
1350 (vc-file-clearprops file)
1351 (or vc-default-back-end
1352 (setq vc-default-back-end (if (vc-find-binary "rcs") 'RCS 'SCCS)))
1353 (message "Registering %s..." file)
1354 (let ((backend
1355 (cond
1356 ((file-exists-p (vc-backend-subdirectory-name)) vc-default-back-end)
1357 ((file-exists-p "RCS") 'RCS)
1358 ((file-exists-p "SCCS") 'SCCS)
1359 (t vc-default-back-end))))
1360 (cond ((eq backend 'SCCS)
1361 (vc-do-command 0 "admin" file ;; SCCS
1362 (and rev (concat "-r" rev))
1363 "-fb"
1364 (concat "-i" file)
1365 (and comment (concat "-y" comment))
1366 (format
1367 (car (rassq 'SCCS vc-master-templates))
1368 (or (file-name-directory file) "")
1369 (file-name-nondirectory file)))
1370 (delete-file file)
1371 (if vc-keep-workfiles
1372 (vc-do-command 0 "get" file)))
1373 ((eq backend 'RCS)
1374 (vc-do-command 0 "ci" file ;; RCS
1375 (concat (if vc-keep-workfiles "-u" "-r") rev)
1376 (and comment (concat "-t-" comment))
1377 file)
1378 )))
1379 (message "Registering %s...done" file)
1380 )
1381
1382(defun vc-backend-checkout (file &optional writeable rev)
1383 ;; Retrieve a copy of a saved version into a workfile
a9a59766 1384 (message "Checking out %s..." file)
594722a8
ER
1385 (vc-backend-dispatch file
1386 (progn
1387 (vc-do-command 0 "get" file ;; SCCS
1388 (if writeable "-e")
1389 (and rev (concat "-r" (vc-lookup-triple file rev))))
1390 )
1391 (vc-do-command 0 "co" file ;; RCS
1392 (if writeable "-l")
1393 (and rev (concat "-r" rev)))
1394 )
1395 (vc-file-setprop file 'vc-checkout-time (nth 5 (file-attributes file)))
1396 (message "Checking out %s...done" file)
1397 )
1398
1399(defun vc-backend-logentry-check (file)
1400 (vc-backend-dispatch file
8c0aaf40 1401 (if (>= (buffer-size) 512) ;; SCCS
594722a8
ER
1402 (progn
1403 (goto-char 512)
1404 (error
1405 "Log must be less than 512 characters. Point is now at char 512.")))
1406 nil)
1407 )
1408
1409(defun vc-backend-checkin (file &optional rev comment)
1410 ;; Register changes to FILE as level REV with explanatory COMMENT.
1411 ;; Automatically retrieves a read-only version of the file with
1412 ;; keywords expanded if vc-keep-workfiles is non-nil, otherwise
1413 ;; it deletes the workfile.
1414 (message "Checking in %s..." file)
2ea8ce47
RM
1415 (save-excursion
1416 ;; Change buffers to get local value of vc-checkin-switches.
1417 (set-buffer (or (get-file-buffer file) (current-buffer)))
1418 (vc-backend-dispatch file
1419 (progn
1420 (apply 'vc-do-command 0 "delta" file
1421 (if rev (concat "-r" rev))
1422 (concat "-y" comment)
1423 vc-checkin-switches)
1424 (if vc-keep-workfiles
1425 (vc-do-command 0 "get" file))
1426 )
1427 (apply 'vc-do-command 0 "ci" file
1428 (concat (if vc-keep-workfiles "-u" "-r") rev)
1429 (concat "-m" comment)
1430 vc-checkin-switches)
1431 ))
594722a8
ER
1432 (vc-file-setprop file 'vc-locking-user nil)
1433 (message "Checking in %s...done" file)
1434 )
1435
1436(defun vc-backend-revert (file)
1437 ;; Revert file to latest checked-in version.
1438 (message "Reverting %s..." file)
1439 (vc-backend-dispatch
1440 file
1441 (progn ;; SCCS
1442 (vc-do-command 0 "unget" file nil)
1443 (vc-do-command 0 "get" file nil))
1444 (progn
1445 (delete-file file) ;; RCS
1446 (vc-do-command 0 "co" file "-u")))
1447 (vc-file-setprop file 'vc-locking-user nil)
1448 (message "Reverting %s...done" file)
1449 )
1450
1451(defun vc-backend-steal (file &optional rev)
1452 ;; Steal the lock on the current workfile. Needs RCS 5.6.2 or later for -M.
1453 (message "Stealing lock on %s..." file)
1454 (progn
1455 (vc-do-command 0 "unget" file "-n" (if rev (concat "-r" rev)))
1456 (vc-do-command 0 "get" file "-g" (if rev (concat "-r" rev)))
1457 )
1458 (progn
73ce9046 1459 (vc-do-command 0 "rcs" "-M" (concat "-u" rev) file)
d743a3c8 1460 (delete-file file)
73ce9046 1461 (vc-do-command 0 "rcs" (concat "-l" rev) file)
594722a8
ER
1462 )
1463 (vc-file-setprop file 'vc-locking-user (user-login-name))
1464 (message "Stealing lock on %s...done" file)
1465 )
1466
1467(defun vc-backend-uncheck (file target)
1468 ;; Undo the latest checkin. Note: this code will have to get a lot
1469 ;; smarter when we support multiple branches.
1470 (message "Removing last change from %s..." file)
1471 (vc-backend-dispatch file
1472 (vc-do-command 0 "rmdel" file (concat "-r" target))
1473 (vc-do-command 0 "rcs" file (concat "-o" target))
1474 )
1475 (message "Removing last change from %s...done" file)
1476 )
1477
1478(defun vc-backend-print-log (file)
1479 ;; Print change log associated with FILE to buffer *vc*.
1480 (vc-do-command 0
1481 (vc-backend-dispatch file "prs" "rlog")
1482 file)
1483 )
1484
1485(defun vc-backend-assign-name (file name)
1486 ;; Assign to a FILE's latest version a given NAME.
1487 (vc-backend-dispatch file
1488 (vc-add-triple name file (vc-latest-version file)) ;; SCCS
1489 (vc-do-command 0 "rcs" file (concat "-n" name ":")) ;; RCS
3234e2a3 1490 )
3234e2a3 1491 )
594722a8
ER
1492
1493(defun vc-backend-diff (file oldvers &optional newvers)
1494 ;; Get a difference report between two versions
7b4f934d
ER
1495 (if (eq (vc-backend-deduce file) 'SCCS)
1496 (setq oldvers (vc-lookup-triple file oldvers))
1497 (setq newvers (vc-lookup-triple file newvers)))
594722a8
ER
1498 (apply 'vc-do-command 1
1499 (or (vc-backend-dispatch file "vcdiff" "rcsdiff")
7ef84cf9 1500 (vc-registration-error file))
594722a8
ER
1501 file
1502 (and oldvers (concat "-r" oldvers))
1503 (and newvers (concat "-r" newvers))
4c2c1de1
RS
1504 (if (listp diff-switches)
1505 diff-switches
1506 (list diff-switches))
594722a8
ER
1507 ))
1508
1509(defun vc-check-headers ()
1510 "Check if the current file has any headers in it."
1511 (interactive)
1512 (save-excursion
1513 (goto-char (point-min))
1514 (vc-backend-dispatch buffer-file-name
1515 (re-search-forward "%[MIRLBSDHTEGUYFPQCZWA]%" nil t) ;; SCCS
1516 (re-search-forward "\\$[A-Za-z\300-\326\330-\366\370-\377]+\\(: [\t -#%-\176\240-\377]*\\)?\\$" nil t) ;; RCS
1517 )
1518 ))
1519
1520;; Back-end-dependent stuff ends here.
1521
1522;; Set up key bindings for use while editing log messages
1523
1524(defun vc-log-mode ()
1525 "Minor mode for driving version-control tools.
1526These bindings are added to the global keymap when you enter this mode:
1527\\[vc-next-action] perform next logical version-control operation on current file
1528\\[vc-register] register current file
1529\\[vc-toggle-read-only] like next-action, but won't register files
1530\\[vc-insert-headers] insert version-control headers in current file
1531\\[vc-print-log] display change history of current file
1532\\[vc-revert-buffer] revert buffer to latest version
1533\\[vc-cancel-version] undo latest checkin
1534\\[vc-diff] show diffs between file versions
1535\\[vc-directory] show all files locked by any user in or below .
1536\\[vc-update-change-log] add change log entry from recent checkins
1537
1538While you are entering a change log message for a version, the following
1539additional bindings will be in effect.
1540
1541\\[vc-finish-logentry] proceed with check in, ending log message entry
1542
1543Whenever you do a checkin, your log comment is added to a ring of
1544saved comments. These can be recalled as follows:
1545
1546\\[vc-next-comment] replace region with next message in comment ring
1547\\[vc-previous-comment] replace region with previous message in comment ring
8c0aaf40
ER
1548\\[vc-comment-search-reverse] search backward for regexp in the comment ring
1549\\[vc-comment-search-forward] search backward for regexp in the comment ring
594722a8
ER
1550
1551Entry to the change-log submode calls the value of text-mode-hook, then
1552the value of vc-log-mode-hook.
1553
1554Global user options:
1555 vc-initial-comment If non-nil, require user to enter a change
1556 comment upon first checkin of the file.
1557
1558 vc-keep-workfiles Non-nil value prevents workfiles from being
1559 deleted when changes are checked in
1560
1561 vc-suppress-confirm Suppresses some confirmation prompts,
1562 notably for reversions.
1563
7b4f934d 1564 vc-header-alist Which keywords to insert when adding headers
594722a8
ER
1565 with \\[vc-insert-headers]. Defaults to
1566 '(\"\%\W\%\") under SCCS, '(\"\$Id\$\") under RCS.
1567
1568 vc-static-header-alist By default, version headers inserted in C files
1569 get stuffed in a static string area so that
1570 ident(RCS) or what(SCCS) can see them in the
1571 compiled object code. You can override this
1572 by setting this variable to nil, or change
1573 the header template by changing it.
1574
1575 vc-command-messages if non-nil, display run messages from the
1576 actual version-control utilities (this is
1577 intended primarily for people hacking vc
1578 itself).
1579"
1580 (interactive)
1581 (set-syntax-table text-mode-syntax-table)
1582 (use-local-map vc-log-entry-mode)
1583 (setq local-abbrev-table text-mode-abbrev-table)
1584 (setq major-mode 'vc-log-mode)
1585 (setq mode-name "VC-Log")
1586 (make-local-variable 'vc-log-file)
1587 (make-local-variable 'vc-log-version)
8c0aaf40 1588 (make-local-variable 'vc-comment-ring-index)
594722a8
ER
1589 (set-buffer-modified-p nil)
1590 (setq buffer-file-name nil)
1591 (run-hooks 'text-mode-hook 'vc-log-mode-hook)
1592)
1593
1594;; Initialization code, to be done just once at load-time
1595(if vc-log-entry-mode
1596 nil
1597 (setq vc-log-entry-mode (make-sparse-keymap))
1598 (define-key vc-log-entry-mode "\M-n" 'vc-next-comment)
1599 (define-key vc-log-entry-mode "\M-p" 'vc-previous-comment)
8c0aaf40 1600 (define-key vc-log-entry-mode "\M-r" 'vc-comment-search-reverse)
594722a8
ER
1601 (define-key vc-log-entry-mode "\M-s" 'vc-comment-search-forward)
1602 (define-key vc-log-entry-mode "\C-c\C-c" 'vc-finish-logentry)
1603 )
1604
1605;;; These things should probably be generally available
1606
1607(defun vc-shrink-to-fit ()
1608 "Shrink a window vertically until it's just large enough to contain its text"
1609 (let ((minsize (1+ (count-lines (point-min) (point-max)))))
1610 (if (< minsize (window-height))
1611 (let ((window-min-height 2))
1612 (shrink-window (- (window-height) minsize))))))
1613
02da6253
PE
1614(defun vc-file-tree-walk (func &rest args)
1615 "Walk recursively through default directory,
1616invoking FUNC f ARGS on all non-directory files f underneath it."
1617 (vc-file-tree-walk-internal default-directory func args)
1618 (message "Traversing directory %s...done" default-directory))
1619
1620(defun vc-file-tree-walk-internal (file func args)
1621 (if (not (file-directory-p file))
1622 (apply func file args)
1623 (message "Traversing directory %s..." file)
1624 (let ((dir (file-name-as-directory file)))
1625 (mapcar
1626 (function
1627 (lambda (f) (or
1628 (string-equal f ".")
1629 (string-equal f "..")
1630 (let ((dirf (concat dir f)))
1631 (or
1632 (file-symlink-p dirf) ;; Avoid possible loops
1633 (vc-file-tree-walk-internal dirf func args))))))
1634 (directory-files dir)))))
594722a8
ER
1635
1636(provide 'vc)
1637
1638;;; DEVELOPER'S NOTES ON CONCURRENCY PROBLEMS IN THIS CODE
1639;;;
1640;;; These may be useful to anyone who has to debug or extend the package.
1641;;;
1642;;; A fundamental problem in VC is that there are time windows between
1643;;; vc-next-action's computations of the file's version-control state and
1644;;; the actions that change it. This is a window open to lossage in a
1645;;; multi-user environment; someone else could nip in and change the state
1646;;; of the master during it.
1647;;;
1648;;; The performance problem is that rlog/prs calls are very expensive; we want
1649;;; to avoid them as much as possible.
1650;;;
1651;;; ANALYSIS:
1652;;;
1653;;; The performance problem, it turns out, simplifies in practice to the
1654;;; problem of making vc-locking-user fast. The two other functions that call
1655;;; prs/rlog will not be so commonly used that the slowdown is a problem; one
1656;;; makes snapshots, the other deletes the calling user's last change in the
1657;;; master.
1658;;;
1659;;; The race condition implies that we have to either (a) lock the master
1660;;; during the entire execution of vc-next-action, or (b) detect and
1661;;; recover from errors resulting from dispatch on an out-of-date state.
1662;;;
1663;;; Alternative (a) appears to be unfeasible. The problem is that we can't
1664;;; guarantee that the lock will ever be removed. Suppose a user starts a
1665;;; checkin, the change message buffer pops up, and the user, having wandered
1666;;; off to do something else, simply forgets about it?
1667;;;
1668;;; Alternative (b), on the other hand, works well with a cheap way to speed up
1669;;; vc-locking-user. Usually, if a file is registered, we can read its locked/
1670;;; unlocked state and its current owner from its permissions.
1671;;;
1672;;; This shortcut will fail if someone has manually changed the workfile's
1673;;; permissions; also if developers are munging the workfile in several
1674;;; directories, with symlinks to a master (in this latter case, the
1675;;; permissions shortcut will fail to detect a lock asserted from another
1676;;; directory).
1677;;;
1678;;; Note that these cases correspond exactly to the errors which could happen
1679;;; because of a competing checkin/checkout race in between two instances of
1680;;; vc-next-action.
1681;;;
1682;;; For VC's purposes, a workfile/master pair may have the following states:
1683;;;
1684;;; A. Unregistered. There is a workfile, there is no master.
1685;;;
1686;;; B. Registered and not locked by anyone.
1687;;;
1688;;; C. Locked by calling user and unchanged.
1689;;;
1690;;; D. Locked by the calling user and changed.
1691;;;
1692;;; E. Locked by someone other than the calling user.
1693;;;
1694;;; This makes for 25 states and 20 error conditions. Here's the matrix:
1695;;;
1696;;; VC's idea of state
1697;;; |
1698;;; V Actual state RCS action SCCS action Effect
1699;;; A B C D E
1700;;; A . 1 2 3 4 ci -u -t- admin -fb -i<file> initial admin
1701;;; B 5 . 6 7 8 co -l get -e checkout
1702;;; C 9 10 . 11 12 co -u unget; get revert
1703;;; D 13 14 15 . 16 ci -u -m<comment> delta -y<comment>; get checkin
1704;;; E 17 18 19 20 . rcs -u -M ; rcs -l unget -n ; get -g steal lock
1705;;;
1706;;; All commands take the master file name as a last argument (not shown).
1707;;;
1708;;; In the discussion below, a "self-race" is a pathological situation in
1709;;; which VC operations are being attempted simultaneously by two or more
1710;;; Emacsen running under the same username.
1711;;;
1712;;; The vc-next-action code has the following windows:
1713;;;
1714;;; Window P:
1715;;; Between the check for existence of a master file and the call to
1716;;; admin/checkin in vc-buffer-admin (apparent state A). This window may
1717;;; never close if the initial-comment feature is on.
1718;;;
1719;;; Window Q:
1720;;; Between the call to vc-workfile-unchanged-p in and the immediately
1721;;; following revert (apparent state C).
1722;;;
1723;;; Window R:
1724;;; Between the call to vc-workfile-unchanged-p in and the following
1725;;; checkin (apparent state D). This window may never close.
1726;;;
1727;;; Window S:
1728;;; Between the unlock and the immediately following checkout during a
1729;;; revert operation (apparent state C). Included in window Q.
1730;;;
1731;;; Window T:
1732;;; Between vc-locking-user and the following checkout (apparent state B).
1733;;;
1734;;; Window U:
1735;;; Between vc-locking-user and the following revert (apparent state C).
1736;;; Includes windows Q and S.
1737;;;
1738;;; Window V:
1739;;; Between vc-locking-user and the following checkin (apparent state
1740;;; D). This window may never be closed if the user fails to complete the
1741;;; checkin message. Includes window R.
1742;;;
1743;;; Window W:
1744;;; Between vc-locking-user and the following steal-lock (apparent
1745;;; state E). This window may never cloce if the user fails to complete
1746;;; the steal-lock message. Includes window X.
1747;;;
1748;;; Window X:
1749;;; Between the unlock and the immediately following re-lock during a
1750;;; steal-lock operation (apparent state E). This window may never cloce
1751;;; if the user fails to complete the steal-lock message.
1752;;;
1753;;; Errors:
1754;;;
1755;;; Apparent state A ---
1756;;;
1757;;; 1. File looked unregistered but is actually registered and not locked.
1758;;;
1759;;; Potential cause: someone else's admin during window P, with
1760;;; caller's admin happening before their checkout.
1761;;;
1762;;; RCS: ci will fail with a "no lock set by <user>" message.
1763;;; SCCS: admin will fail with error (ad19).
1764;;;
1765;;; We can let these errors be passed up to the user.
1766;;;
1767;;; 2. File looked unregistered but is actually locked by caller, unchanged.
1768;;;
1769;;; Potential cause: self-race during window P.
1770;;;
1771;;; RCS: will revert the file to the last saved version and unlock it.
1772;;; SCCS: will fail with error (ad19).
1773;;;
1774;;; Either of these consequences is acceptable.
1775;;;
1776;;; 3. File looked unregistered but is actually locked by caller, changed.
1777;;;
1778;;; Potential cause: self-race during window P.
1779;;;
1780;;; RCS: will register the caller's workfile as a delta with a
1781;;; null change comment (the -t- switch will be ignored).
1782;;; SCCS: will fail with error (ad19).
1783;;;
1784;;; 4. File looked unregistered but is locked by someone else.
1785;;;
1786;;; Potential cause: someone else's admin during window P, with
1787;;; caller's admin happening *after* their checkout.
1788;;;
1789;;; RCS: will fail with a "no lock set by <user>" message.
1790;;; SCCS: will fail with error (ad19).
1791;;;
1792;;; We can let these errors be passed up to the user.
1793;;;
1794;;; Apparent state B ---
1795;;;
1796;;; 5. File looked registered and not locked, but is actually unregistered.
1797;;;
1798;;; Potential cause: master file got nuked during window P.
1799;;;
1800;;; RCS: will fail with "RCS/<file>: No such file or directory"
1801;;; SCCS: will fail with error ut4.
1802;;;
1803;;; We can let these errors be passed up to the user.
1804;;;
1805;;; 6. File looked registered and not locked, but is actually locked by the
1806;;; calling user and unchanged.
1807;;;
1808;;; Potential cause: self-race during window T.
1809;;;
1810;;; RCS: in the same directory as the previous workfile, co -l will fail
1811;;; with "co error: writable foo exists; checkout aborted". In any other
1812;;; directory, checkout will succeed.
1813;;; SCCS: will fail with ge17.
1814;;;
1815;;; Either of these consequences is acceptable.
1816;;;
1817;;; 7. File looked registered and not locked, but is actually locked by the
1818;;; calling user and changed.
1819;;;
1820;;; As case 6.
1821;;;
1822;;; 8. File looked registered and not locked, but is actually locked by another
1823;;; user.
1824;;;
1825;;; Potential cause: someone else checks it out during window T.
1826;;;
1827;;; RCS: co error: revision 1.3 already locked by <user>
1828;;; SCCS: fails with ge4 (in directory) or ut7 (outside it).
1829;;;
1830;;; We can let these errors be passed up to the user.
1831;;;
1832;;; Apparent state C ---
1833;;;
1834;;; 9. File looks locked by calling user and unchanged, but is unregistered.
1835;;;
1836;;; As case 5.
1837;;;
1838;;; 10. File looks locked by calling user and unchanged, but is actually not
1839;;; locked.
1840;;;
1841;;; Potential cause: a self-race in window U, or by the revert's
1842;;; landing during window X of some other user's steal-lock or window S
1843;;; of another user's revert.
1844;;;
1845;;; RCS: succeeds, refreshing the file from the identical version in
1846;;; the master.
1847;;; SCCS: fails with error ut4 (p file nonexistent).
1848;;;
1849;;; Either of these consequences is acceptable.
1850;;;
1851;;; 11. File is locked by calling user. It looks unchanged, but is actually
1852;;; changed.
1853;;;
1854;;; Potential cause: the file would have to be touched by a self-race
1855;;; during window Q.
1856;;;
1857;;; The revert will succeed, removing whatever changes came with
1858;;; the touch. It is theoretically possible that work could be lost.
1859;;;
1860;;; 12. File looks like it's locked by the calling user and unchanged, but
1861;;; it's actually locked by someone else.
1862;;;
1863;;; Potential cause: a steal-lock in window V.
1864;;;
1865;;; RCS: co error: revision <rev> locked by <user>; use co -r or rcs -u
1866;;; SCCS: fails with error un2
1867;;;
1868;;; We can pass these errors up to the user.
1869;;;
1870;;; Apparent state D ---
1871;;;
1872;;; 13. File looks like it's locked by the calling user and changed, but it's
1873;;; actually unregistered.
1874;;;
1875;;; Potential cause: master file got nuked during window P.
1876;;;
1877;;; RCS: Checks in the user's version as an initial delta.
1878;;; SCCS: will fail with error ut4.
1879;;;
1880;;; This case is kind of nasty. It means VC may fail to detect the
1881;;; loss of previous version information.
1882;;;
1883;;; 14. File looks like it's locked by the calling user and changed, but it's
1884;;; actually unlocked.
1885;;;
1886;;; Potential cause: self-race in window V, or the checkin happening
1887;;; during the window X of someone else's steal-lock or window S of
1888;;; someone else's revert.
1889;;;
1890;;; RCS: ci will fail with "no lock set by <user>".
1891;;; SCCS: delta will fail with error ut4.
1892;;;
1893;;; 15. File looks like it's locked by the calling user and changed, but it's
1894;;; actually locked by the calling user and unchanged.
1895;;;
1896;;; Potential cause: another self-race --- a whole checkin/checkout
1897;;; sequence by the calling user would have to land in window R.
1898;;;
1899;;; SCCS: checks in a redundant delta and leaves the file unlocked as usual.
1900;;; RCS: reverts to the file state as of the second user's checkin, leaving
1901;;; the file unlocked.
1902;;;
1903;;; It is theoretically possible that work could be lost under RCS.
1904;;;
1905;;; 16. File looks like it's locked by the calling user and changed, but it's
1906;;; actually locked by a different user.
1907;;;
1908;;; RCS: ci error: no lock set by <user>
1909;;; SCCS: unget will fail with error un2
1910;;;
1911;;; We can pass these errors up to the user.
1912;;;
1913;;; Apparent state E ---
1914;;;
1915;;; 17. File looks like it's locked by some other user, but it's actually
1916;;; unregistered.
1917;;;
1918;;; As case 13.
1919;;;
1920;;; 18. File looks like it's locked by some other user, but it's actually
1921;;; unlocked.
1922;;;
1923;;; Potential cause: someone released a lock during window W.
1924;;;
1925;;; RCS: The calling user will get the lock on the file.
1926;;; SCCS: unget -n will fail with cm4.
1927;;;
1928;;; Either of these consequences will be OK.
1929;;;
1930;;; 19. File looks like it's locked by some other user, but it's actually
1931;;; locked by the calling user and unchanged.
1932;;;
1933;;; Potential cause: the other user relinquishing a lock followed by
1934;;; a self-race, both in window W.
1935;;;
1936;;; Under both RCS and SCCS, both unlock and lock will succeed, making
1937;;; the sequence a no-op.
1938;;;
1939;;; 20. File looks like it's locked by some other user, but it's actually
1940;;; locked by the calling user and changed.
1941;;;
1942;;; As case 19.
1943;;;
1944;;; PROBLEM CASES:
1945;;;
1946;;; In order of decreasing severity:
1947;;;
1948;;; Cases 11 and 15 under RCS are the only one that potentially lose work.
1949;;; They would require a self-race for this to happen.
1950;;;
1951;;; Case 13 in RCS loses information about previous deltas, retaining
1952;;; only the information in the current workfile. This can only happen
1953;;; if the master file gets nuked in window P.
1954;;;
1955;;; Case 3 in RCS and case 15 under SCCS insert a redundant delta with
1956;;; no change comment in the master. This would require a self-race in
1957;;; window P or R respectively.
1958;;;
1959;;; Cases 2, 10, 19 and 20 do extra work, but make no changes.
1960;;;
1961;;; Unfortunately, it appears to me that no recovery is possible in these
1962;;; cases. They don't yield error messages, so there's no way to tell that
1963;;; a race condition has occurred.
1964;;;
1965;;; All other cases don't change either the workfile or the master, and
1966;;; trigger command errors which the user will see.
1967;;;
1968;;; Thus, there is no explicit recovery code.
1969
1970;;; vc.el ends here