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