(eshell-du-prefer-over-ange): Doc fix.
[bpt/emacs.git] / lisp / vc.el
CommitLineData
594722a8
ER
1;;; vc.el --- drive a version-control system from within Emacs
2
0e362f54 3;; Copyright (C) 1992,93,94,95,96,97,98,2000 Free Software Foundation, Inc.
594722a8 4
0e362f54
GM
5;; Author: FSF (see below for full credits)
6;; Maintainer: Andre Spiegel <spiegel@gnu.org>
594722a8 7
46e33aee 8;; $Id: vc.el,v 1.279 2000/10/08 19:12:52 monnier Exp $
045e1aa5 9
594722a8
ER
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software; you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation; either version 2, or (at your option)
15;; any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
b578f267
EN
23;; along with GNU Emacs; see the file COPYING. If not, write to the
24;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25;; Boston, MA 02111-1307, USA.
594722a8 26
0e362f54
GM
27;;; Credits:
28
29;; VC was initially designed and implemented by Eric S. Raymond
30;; <esr@snark.thyrsus.com>. Over the years, many people have
31;; contributed substantial amounts of work to VC. These include:
32;; Per Cederqvist <ceder@lysator.liu.se>
33;; Paul Eggert <eggert@twinsun.com>
34;; Sebastian Kremer <sk@thp.uni-koeln.de>
35;; Martin Lorentzson <martinl@gnu.org>
6f41eeb5 36;; Dave Love <d.love@gnu.org>
0e362f54
GM
37;; Stefan Monnier <monnier@cs.yale.edu>
38;; Andre Spiegel <spiegel@gnu.org>
39;; Richard Stallman <rms@gnu.org>
40;; ttn@netcom.com
41
594722a8
ER
42;;; Commentary:
43
1a2f456b
ER
44;; This mode is fully documented in the Emacs user's manual.
45;;
632e9525 46;; Supported version-control systems presently include SCCS, RCS, and CVS.
b0c9bc8c
AS
47;;
48;; Some features will not work with old RCS versions. Where
49;; appropriate, VC finds out which version you have, and allows or
0e362f54 50;; disallows those features (stealing locks, for example, works only
b0c9bc8c 51;; from 5.6.2 onwards).
632e9525
RS
52;; Even initial checkins will fail if your RCS version is so old that ci
53;; doesn't understand -t-; this has been known to happen to people running
0e362f54 54;; NExTSTEP 3.0.
594722a8 55;;
0e362f54 56;; You can support the RCS -x option by customizing vc-rcs-master-templates.
594722a8
ER
57;;
58;; Proper function of the SCCS diff commands requires the shellscript vcdiff
59;; to be installed somewhere on Emacs's path for executables.
60;;
1a2f456b 61;; If your site uses the ChangeLog convention supported by Emacs, the
594be62e 62;; function vc-comment-to-change-log should prove a useful checkin hook.
1a2f456b 63;;
594722a8
ER
64;; The vc code maintains some internal state in order to reduce expensive
65;; version-control operations to a minimum. Some names are only computed
34291cd2 66;; once. If you perform version control operations with RCS/SCCS/CVS while
594722a8
ER
67;; vc's back is turned, or move/rename master files while vc is running,
68;; vc may get seriously confused. Don't do these things!
69;;
70;; Developer's notes on some concurrency issues are included at the end of
71;; the file.
72
73;;; Code:
74
0e362f54
GM
75;;;;;;;;;;;;;;;;; Backend-specific functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
76;;
77;; for each operation FUN, the backend should provide a function vc-BACKEND-FUN.
1d502d5a 78;; Operations marked with a `-' instead of a `*' are optional.
0e362f54
GM
79
80;; * registered (file)
81;; * state (file)
82;; - state-heuristic (file)
83;; The default behavior delegates to `state'.
84;; - dir-state (dir)
85;; * checkout-model (file)
86;; - mode-line-string (file)
87;; * workfile-version (file)
88;; * revert (file)
099bd78a
SM
89;; - merge-news (file)
90;; Only needed if state `needs-merge' is possible.
91;; - merge (file rev1 rev2)
92;; - steal-lock (file &optional version)
93;; Only required if files can be locked by somebody else.
0e362f54 94;; * register (file rev comment)
1d502d5a 95;; * unregister (file backend)
72cfc5fb 96;; - receive-file (file rev)
099bd78a 97;; - responsible-p (file)
0e362f54
GM
98;; Should also work if FILE is a directory (ends with a slash).
99;; - could-register (file)
100;; * checkout (file writable &optional rev destfile)
101;; Checkout revision REV of FILE into DESTFILE.
102;; DESTFILE defaults to FILE.
103;; The file should be made writable if WRITABLE is non-nil.
104;; REV can be nil (BASE) or "" (HEAD) or any other revision.
105;; * checkin (file rev comment)
106;; - logentry-check ()
107;; * diff (file &optional rev1 rev2)
108;; Insert the diff for FILE into the current buffer.
109;; REV1 should default to workfile-version.
110;; REV2 should default to the current workfile
111;; Return a status of either 0 (i.e. no diff) or 1 (i.e. either non-empty
112;; diff or the diff is run asynchronously).
113;; - workfile-unchanged-p (file)
114;; Return non-nil if FILE is unchanged from its current workfile version.
115;; This function should do a brief comparison of FILE's contents
116;; with those of the master version. If the backend does not have
117;; such a brief-comparison feature, the default implementation of this
118;; function can be used, which delegates to a full vc-BACKEND-diff.
119;; - clear-headers ()
120;; * check-headers ()
121;; - dired-state-info (file)
122;; - create-snapshot (dir name branchp)
123;; Take a snapshot of the current state of files under DIR and name it NAME.
124;; This should make sure that files are up-to-date before proceeding
125;; with the action.
126;; DIR can also be a file and if BRANCHP is specified, NAME
127;; should be created as a branch and DIR should be checked out under
128;; this new branch. The default behavior does not support branches
129;; but does a sanity check, a tree traversal and for each file calls
130;; `assign-name'.
131;; * assign-name (file name)
132;; Give name NAME to the current version of FILE, assuming it is
133;; up-to-date. Only used by the default version of `create-snapshot'.
134;; - retrieve-snapshot (dir name update)
135;; Retrieve a named snapshot of all registered files at or below DIR.
6f41eeb5 136;; If UPDATE is non-nil, then update buffers of any files in the snapshot
0e362f54
GM
137;; that are currently visited.
138;; * print-log (file)
139;; Insert the revision log of FILE into the current buffer.
140;; - show-log-entry (version)
ffda0460
AS
141;; - wash-log (file)
142;; Remove all non-comment information from the output of print-log
1d502d5a 143;; - comment-history (file)
0e362f54
GM
144;; - update-changelog (files)
145;; Find changelog entries for FILES, or for all files at or below
146;; the default-directory if FILES is nil.
147;; * latest-on-branch-p (file)
099bd78a
SM
148;; - cancel-version (file writable)
149;; - rename-file (old new)
150;; - annotate-command (file buf)
151;; - annotate-difference (pos)
152;; Only required if `annotate-command' is defined for the backend.
0e362f54 153
594722a8 154(require 'vc-hooks)
8c0aaf40 155(require 'ring)
0e362f54 156(eval-when-compile
7849e179 157 (require 'cl)
099bd78a
SM
158 (require 'compile)
159 (require 'dired) ; for dired-map-over-marks macro
160 (require 'dired-aux)) ; for dired-kill-{line,tree}
8c0aaf40
ER
161
162(if (not (assoc 'vc-parent-buffer minor-mode-alist))
163 (setq minor-mode-alist
164 (cons '(vc-parent-buffer vc-parent-buffer-name)
165 minor-mode-alist)))
594722a8
ER
166
167;; General customization
168
0101cc40
RS
169(defgroup vc nil
170 "Version-control system in Emacs."
171 :group 'tools)
172
173(defcustom vc-suppress-confirm nil
174 "*If non-nil, treat user as expert; suppress yes-no prompts on some things."
175 :type 'boolean
176 :group 'vc)
177
2c4eea90
KH
178(defcustom vc-delete-logbuf-window t
179 "*If non-nil, delete the *VC-log* buffer and window after each logical action.
180If nil, bury that buffer instead.
181This is most useful if you have multiple windows on a frame and would like to
182preserve the setting."
183 :type 'boolean
184 :group 'vc)
185
0101cc40
RS
186(defcustom vc-initial-comment nil
187 "*If non-nil, prompt for initial comment when a file is registered."
188 :type 'boolean
189 :group 'vc)
190
0d53f466
AS
191(defcustom vc-default-init-version "1.1"
192 "*A string used as the default version number when a new file is registered.
0e362f54 193This can be overridden by giving a prefix argument to \\[vc-register]."
0d53f466 194 :type 'string
cd32a7ba
DN
195 :group 'vc
196 :version "20.3")
0d53f466 197
0101cc40
RS
198(defcustom vc-command-messages nil
199 "*If non-nil, display run messages from back-end commands."
200 :type 'boolean
201 :group 'vc)
202
203(defcustom vc-checkin-switches nil
204 "*A string or list of strings specifying extra switches for checkin.
205These are passed to the checkin program by \\[vc-checkin]."
206 :type '(choice (const :tag "None" nil)
207 (string :tag "Argument String")
208 (repeat :tag "Argument List"
209 :value ("")
210 string))
211 :group 'vc)
212
213(defcustom vc-checkout-switches nil
214 "*A string or list of strings specifying extra switches for checkout.
215These are passed to the checkout program by \\[vc-checkout]."
216 :type '(choice (const :tag "None" nil)
217 (string :tag "Argument String")
218 (repeat :tag "Argument List"
219 :value ("")
220 string))
221 :group 'vc)
222
223(defcustom vc-register-switches nil
224 "*A string or list of strings; extra switches for registering a file.
225These are passed to the checkin program by \\[vc-register]."
226 :type '(choice (const :tag "None" nil)
227 (string :tag "Argument String")
228 (repeat :tag "Argument List"
229 :value ("")
230 string))
231 :group 'vc)
232
0e362f54
GM
233(defcustom vc-dired-listing-switches "-al"
234 "*Switches passed to `ls' for vc-dired. MUST contain the `l' option."
235 :type 'string
236 :group 'vc
6f41eeb5 237 :version "21.1")
0e362f54 238
3b574573
AS
239(defcustom vc-dired-recurse t
240 "*If non-nil, show directory trees recursively in VC Dired."
241 :type 'boolean
242 :group 'vc
243 :version "20.3")
244
245(defcustom vc-dired-terse-display t
246 "*If non-nil, show only locked files in VC Dired."
247 :type 'boolean
248 :group 'vc
249 :version "20.3")
250
0101cc40
RS
251(defcustom vc-directory-exclusion-list '("SCCS" "RCS" "CVS")
252 "*List of directory names to be ignored while recursively walking file trees."
253 :type '(repeat string)
254 :group 'vc)
666a0ebb 255
8c0aaf40
ER
256(defconst vc-maximum-comment-ring-size 32
257 "Maximum number of saved comments in the comment ring.")
258
2e810285
RS
259;;; This is duplicated in diff.el.
260(defvar diff-switches "-c"
0e362f54
GM
261 "*A string or list of strings specifying switches to be passed to diff.")
262
263;;;###autoload
264(defcustom vc-checkin-hook nil
265 "*Normal hook (list of functions) run after a checkin is done.
266See `run-hooks'."
267 :type 'hook
268 :options '(vc-comment-to-change-log)
269 :group 'vc)
270
271;;;###autoload
272(defcustom vc-before-checkin-hook nil
273 "*Normal hook (list of functions) run before a file gets checked in.
274See `run-hooks'."
275 :type 'hook
276 :group 'vc)
277
278(defcustom vc-logentry-check-hook nil
279 "*Normal hook run by `vc-backend-logentry-check'.
280Use this to impose your own rules on the entry in addition to any the
281version control backend imposes itself."
282 :type 'hook
283 :group 'vc)
2e810285 284
0e362f54 285;; Annotate customization
7d2d9482
RS
286(defcustom vc-annotate-color-map
287 '(( 26.3672 . "#FF0000")
288 ( 52.7344 . "#FF3800")
289 ( 79.1016 . "#FF7000")
290 (105.4688 . "#FFA800")
291 (131.8359 . "#FFE000")
292 (158.2031 . "#E7FF00")
293 (184.5703 . "#AFFF00")
294 (210.9375 . "#77FF00")
295 (237.3047 . "#3FFF00")
296 (263.6719 . "#07FF00")
297 (290.0391 . "#00FF31")
298 (316.4063 . "#00FF69")
299 (342.7734 . "#00FFA1")
300 (369.1406 . "#00FFD9")
301 (395.5078 . "#00EEFF")
302 (421.8750 . "#00B6FF")
303 (448.2422 . "#007EFF"))
304 "*Association list of age versus color, for \\[vc-annotate].
305Ages are given in units of 2**-16 seconds.
306Default is eighteen steps using a twenty day increment."
0e362f54 307 :type 'alist
7d2d9482
RS
308 :group 'vc)
309
310(defcustom vc-annotate-very-old-color "#0046FF"
311 "*Color for lines older than CAR of last cons in `vc-annotate-color-map'."
312 :type 'string
313 :group 'vc)
314
315(defcustom vc-annotate-background "black"
316 "*Background color for \\[vc-annotate].
317Default color is used if nil."
318 :type 'string
319 :group 'vc)
320
321(defcustom vc-annotate-menu-elements '(2 0.5 0.1 0.01)
322 "*Menu elements for the mode-specific menu of VC-Annotate mode.
323List of factors, used to expand/compress the time scale. See `vc-annotate'."
0e362f54 324 :type '(repeat number)
7d2d9482
RS
325 :group 'vc)
326
0e362f54
GM
327;; vc-annotate functionality (CVS only).
328(defvar vc-annotate-mode nil
329 "Variable indicating if VC-Annotate mode is active.")
f0b188ed 330
0e362f54
GM
331(defvar vc-annotate-mode-map
332 (let ((m (make-sparse-keymap)))
333 (define-key m [menu-bar] (make-sparse-keymap "VC-Annotate"))
334 m)
335 "Local keymap used for VC-Annotate mode.")
67242a23 336
0e362f54
GM
337(defvar vc-annotate-mode-menu nil
338 "Local keymap used for VC-Annotate mode's menu bar menu.")
7d2d9482 339
594722a8
ER
340;; Header-insertion hair
341
0101cc40 342(defcustom vc-static-header-alist
594722a8
ER
343 '(("\\.c$" .
344 "\n#ifndef lint\nstatic char vcid[] = \"\%s\";\n#endif /* lint */\n"))
0e362f54
GM
345 "*Associate static header string templates with file types.
346A \%s in the template is replaced with the first string associated with
099bd78a 347the file's version control type in `vc-header-alist'."
0101cc40
RS
348 :type '(repeat (cons :format "%v"
349 (regexp :tag "File Type")
350 (string :tag "Header String")))
351 :group 'vc)
7b4f934d 352
0101cc40 353(defcustom vc-comment-alist
594722a8
ER
354 '((nroff-mode ".\\\"" ""))
355 "*Special comment delimiters to be used in generating vc headers only.
099bd78a
SM
356Add an entry in this list if you need to override the normal `comment-start'
357and `comment-end' variables. This will only be necessary if the mode language
0101cc40
RS
358is sensitive to blank lines."
359 :type '(repeat (list :format "%v"
360 (symbol :tag "Mode")
361 (string :tag "Comment Start")
362 (string :tag "Comment End")))
363 :group 'vc)
594722a8 364
bbf97570 365;; Default is to be extra careful for super-user.
6f41eeb5
DL
366;; TODO: This variable is no longer used; the corresponding checks
367;; are always done now. If that turns out to be fast enough,
0e362f54 368;; the variable can be obsoleted.
0101cc40 369(defcustom vc-checkout-carefully (= (user-uid) 0)
bbf97570
RS
370 "*Non-nil means be extra-careful in checkout.
371Verify that the file really is not locked
0101cc40
RS
372and that its contents match what the master file says."
373 :type 'boolean
374 :group 'vc)
bbf97570 375
0e362f54
GM
376\f
377;;; The main keymap
378
379(defvar vc-prefix-map
380 (let ((map (make-sparse-keymap)))
381 (define-key map "a" 'vc-update-change-log)
1d502d5a 382 (define-key map "b" 'vc-switch-backend)
0e362f54
GM
383 (define-key map "c" 'vc-cancel-version)
384 (define-key map "d" 'vc-directory)
385 (define-key map "g" 'vc-annotate)
386 (define-key map "h" 'vc-insert-headers)
387 (define-key map "i" 'vc-register)
388 (define-key map "l" 'vc-print-log)
389 (define-key map "m" 'vc-merge)
390 (define-key map "r" 'vc-retrieve-snapshot)
391 (define-key map "s" 'vc-create-snapshot)
392 (define-key map "u" 'vc-revert-buffer)
393 (define-key map "v" 'vc-next-action)
394 (define-key map "=" 'vc-diff)
395 (define-key map "~" 'vc-version-other-window)
396 map))
397(fset 'vc-prefix-map vc-prefix-map)
b0c9bc8c 398
0e362f54
GM
399;; Initialization code, to be done just once at load-time
400(defvar vc-log-mode-map
401 (let ((map (make-sparse-keymap)))
402 (define-key map "\M-n" 'vc-next-comment)
403 (define-key map "\M-p" 'vc-previous-comment)
404 (define-key map "\M-r" 'vc-comment-search-reverse)
405 (define-key map "\M-s" 'vc-comment-search-forward)
406 (define-key map "\C-c\C-c" 'vc-finish-logentry)
407 map))
408;; Compatibility with old name. Should we bother ?
409(defvar vc-log-entry-mode vc-log-mode-map)
b0c9bc8c 410
0e362f54 411\f
594722a8 412;; Variables the user doesn't need to know about.
594722a8 413(defvar vc-log-operation nil)
67242a23 414(defvar vc-log-after-operation-hook nil)
0e362f54 415(defvar vc-annotate-buffers nil
099bd78a
SM
416 "Alist of current \"Annotate\" buffers and their corresponding backends.
417The keys are \(BUFFER . BACKEND\). See also `vc-annotate-get-backend'.")
dbf87856
RS
418;; In a log entry buffer, this is a local variable
419;; that points to the buffer for which it was made
420;; (either a file, or a VC dired buffer).
1a2f456b 421(defvar vc-parent-buffer nil)
0e362f54 422(put 'vc-parent-buffer 'permanent-local t)
8c0aaf40 423(defvar vc-parent-buffer-name nil)
0e362f54 424(put 'vc-parent-buffer-name 'permanent-local t)
594722a8 425
db59472c
RS
426(defvar vc-log-file)
427(defvar vc-log-version)
428
8c0aaf40 429(defvar vc-dired-mode nil)
e1f297e6
ER
430(make-variable-buffer-local 'vc-dired-mode)
431
c9b35ece 432(defvar vc-comment-ring (make-ring vc-maximum-comment-ring-size))
8c0aaf40 433(defvar vc-comment-ring-index nil)
0e362f54 434(defvar vc-last-comment-match "")
c8de1d91 435
0e362f54
GM
436;;; functions that operate on RCS revision numbers. This code should
437;;; also be moved into the backends. It stays for now, however, since
438;;; it is used in code below.
c8de1d91 439(defun vc-trunk-p (rev)
099bd78a 440 "Return t if REV is a revision on the trunk."
c8de1d91
AS
441 (not (eq nil (string-match "\\`[0-9]+\\.[0-9]+\\'" rev))))
442
ccb141e8 443(defun vc-branch-p (rev)
099bd78a 444 "Return t if REV is a branch revision."
ccb141e8
AS
445 (not (eq nil (string-match "\\`[0-9]+\\(\\.[0-9]+\\.[0-9]+\\)*\\'" rev))))
446
c8de1d91 447(defun vc-branch-part (rev)
099bd78a 448 "Return the branch part of a revision number REV."
c8de1d91
AS
449 (substring rev 0 (string-match "\\.[0-9]+\\'" rev)))
450
c0d66cb2 451(defun vc-minor-part (rev)
099bd78a 452 "Return the minor version number of a revision number REV."
c0d66cb2
RS
453 (string-match "[0-9]+\\'" rev)
454 (substring rev (match-beginning 0) (match-end 0)))
455
456(defun vc-previous-version (rev)
099bd78a 457 "Guess the version number immediately preceding REV."
c0d66cb2
RS
458 (let ((branch (vc-branch-part rev))
459 (minor-num (string-to-number (vc-minor-part rev))))
460 (if (> minor-num 1)
461 ;; version does probably not start a branch or release
462 (concat branch "." (number-to-string (1- minor-num)))
463 (if (vc-trunk-p rev)
464 ;; we are at the beginning of the trunk --
465 ;; don't know anything to return here
466 ""
467 ;; we are at the beginning of a branch --
468 ;; return version of starting point
469 (vc-branch-part branch)))))
470
594722a8
ER
471;; File property caching
472
8c0aaf40
ER
473(defun vc-clear-context ()
474 "Clear all cached file properties and the comment ring."
475 (interactive)
476 (fillarray vc-file-prop-obarray nil)
477 ;; Note: there is potential for minor lossage here if there is an open
478 ;; log buffer with a nonzero local value of vc-comment-ring-index.
c9b35ece 479 (setq vc-comment-ring (make-ring vc-maximum-comment-ring-size)))
8c0aaf40 480
099bd78a 481(defmacro with-vc-properties (file form settings)
a3255400
SM
482 "Execute FORM, then set per-file properties for FILE,
483but only those that have not been set during the execution of FORM.
484SETTINGS is a list of two-element lists, each of which has the
485 form (PROPERTY . VALUE)."
099bd78a
SM
486 `(let ((vc-touched-properties (list t))
487 (filename ,file))
488 ,form
489 (mapcar (lambda (setting)
a3255400 490 (let ((property (car setting)))
099bd78a 491 (unless (memq property vc-touched-properties)
46e33aee 492 (put (intern filename vc-file-prop-obarray)
a3255400 493 property (cdr setting)))))
099bd78a
SM
494 ,settings)))
495
594722a8
ER
496;; Random helper functions
497
0e362f54
GM
498(defsubst vc-editable-p (file)
499 (or (eq (vc-checkout-model file) 'implicit)
7849e179 500 (memq (vc-state file) '(edited needs-merge))))
c8de1d91 501
709822e8
AS
502;;; Two macros for elisp programming
503;;;###autoload
504(defmacro with-vc-file (file comment &rest body)
0e362f54
GM
505 "Check out a writable copy of FILE if necessary and execute the body.
506Check in FILE with COMMENT (a string) after BODY has been executed.
507FILE is passed through `expand-file-name'; BODY executed within
508`save-excursion'. If FILE is not under version control, or locked by
709822e8
AS
509somebody else, signal error."
510 `(let ((file (expand-file-name ,file)))
ceec5a0c 511 (or (vc-backend file)
709822e8 512 (error (format "File not under version control: `%s'" file)))
0e362f54
GM
513 (unless (vc-editable-p file)
514 (let ((state (vc-state file)))
515 (if (stringp state) (error (format "`%s' is locking `%s'" state file))
516 (vc-checkout file t))))
709822e8
AS
517 (save-excursion
518 ,@body)
519 (vc-checkin file nil ,comment)))
46e33aee 520(put 'with-vc-file 'indent-function 1)
709822e8
AS
521
522;;;###autoload
523(defmacro edit-vc-file (file comment &rest body)
0e362f54
GM
524 "Edit FILE under version control, executing body.
525Checkin with COMMENT after executing BODY.
709822e8
AS
526This macro uses `with-vc-file', passing args to it.
527However, before executing BODY, find FILE, and after BODY, save buffer."
528 `(with-vc-file
529 ,file ,comment
7849e179 530 (set-buffer (find-file-noselect ,file))
709822e8
AS
531 ,@body
532 (save-buffer)))
46e33aee 533(put 'edit-vc-file 'indent-function 1)
709822e8 534
b6909007 535(defun vc-ensure-vc-buffer ()
099bd78a 536 "Make sure that the current buffer visits a version-controlled file."
b6909007
AS
537 (if vc-dired-mode
538 (set-buffer (find-file-noselect (dired-get-filename)))
539 (while vc-parent-buffer
540 (pop-to-buffer vc-parent-buffer))
541 (if (not (buffer-file-name))
542 (error "Buffer %s is not associated with a file" (buffer-name))
543 (if (not (vc-backend (buffer-file-name)))
544 (error "File %s is not under version control" (buffer-file-name))))))
7ef84cf9 545
594722a8 546(defvar vc-binary-assoc nil)
87a00c4f
EZ
547(defvar vc-binary-suffixes
548 (if (memq system-type '(ms-dos windows-nt))
549 '(".exe" ".com" ".bat" ".cmd" ".btm" "")
550 '("")))
0e362f54
GM
551
552(defun vc-process-filter (p s)
099bd78a 553 "An alternative output filter for async process P.
0e362f54
GM
554The only difference with the default filter is to insert S after markers."
555 (with-current-buffer (process-buffer p)
556 (save-excursion
557 (let ((inhibit-read-only t))
558 (goto-char (process-mark p))
559 (insert s)
560 (set-marker (process-mark p) (point))))))
561
562(defun vc-setup-buffer (&optional buf)
099bd78a 563 "Prepare BUF for executing a VC command and make it the current buffer.
0e362f54
GM
564BUF defaults to \"*vc*\", can be a string and will be created if necessary."
565 (unless buf (setq buf "*vc*"))
566 (let ((camefrom (current-buffer))
567 (olddir default-directory))
568 (set-buffer (get-buffer-create buf))
569 (kill-all-local-variables)
570 (set (make-local-variable 'vc-parent-buffer) camefrom)
571 (set (make-local-variable 'vc-parent-buffer-name)
572 (concat " from " (buffer-name camefrom)))
573 (setq default-directory olddir)
574 (let ((inhibit-read-only t))
575 (erase-buffer))))
576
577(defun vc-exec-after (code)
578 "Eval CODE when the current buffer's process is done.
579If the current buffer has no process, just evaluate CODE.
580Else, add CODE to the process' sentinel."
581 (let ((proc (get-buffer-process (current-buffer))))
582 (cond
583 ;; If there's no background process, just execute the code.
584 ((null proc) (eval code))
585 ;; If the background process has exited, reap it and try again
586 ((eq (process-status proc) 'exit)
587 (delete-process proc)
588 (vc-exec-after code))
589 ;; If a process is running, add CODE to the sentinel
590 ((eq (process-status proc) 'run)
591 (let ((sentinel (process-sentinel proc)))
592 (set-process-sentinel proc
593 `(lambda (p s)
594 (with-current-buffer ',(current-buffer)
595 (goto-char (process-mark p))
6f41eeb5 596 ,@(append (cdr (cdr (cdr ;strip off `with-current-buffer buf
0e362f54
GM
597 ; (goto-char...)'
598 (car (cdr (cdr ;strip off `lambda (p s)'
599 sentinel))))))
600 (list `(vc-exec-after ',code))))))))
601 (t (error "Unexpected process state"))))
602 nil)
603
604(defvar vc-post-command-functions nil
605 "Hook run at the end of `vc-do-command'.
606Each function is called inside the buffer in which the command was run
607and is passed 3 argument: the COMMAND, the FILE and the FLAGS.")
608
609(defun vc-do-command (buffer okstatus command file &rest flags)
099bd78a 610 "Execute a version control command, notifying user and checking for errors.
0e362f54
GM
611Output from COMMAND goes to BUFFER, or *vc* if BUFFER is nil or the current
612buffer (which is assumed to be properly setup) if BUFFER is t. The
a0b87bc1 613command is considered successful if its exit status does not exceed
0e362f54
GM
614OKSTATUS (if OKSTATUS is nil, that means to ignore errors, if it is 'async,
615that means not to wait for termination of the subprocess). FILE is
a0b87bc1 616the name of the working file (may also be nil, to execute commands
6f41eeb5 617that don't expect a file name). If an optional list of FLAGS is present,
0e362f54 618that is inserted into the command line before the filename."
b0c9bc8c 619 (and file (setq file (expand-file-name file)))
594722a8 620 (if vc-command-messages
02da6253 621 (message "Running %s on %s..." command file))
0e362f54
GM
622 (save-current-buffer
623 (unless (eq buffer t) (vc-setup-buffer buffer))
624 (let ((squeezed nil)
625 (inhibit-read-only t)
626 (status 0))
627 (setq squeezed (delq nil (copy-sequence flags)))
628 (when file
629 ;; FIXME: file-relative-name can return a bogus result because
630 ;; it doesn't look at the actual file-system to see if symlinks
631 ;; come into play.
632 (setq squeezed (append squeezed (list (file-relative-name file)))))
633 (let ((exec-path (append vc-path exec-path))
634 ;; Add vc-path to PATH for the execution of this command.
635 (process-environment
636 (cons (concat "PATH=" (getenv "PATH")
637 path-separator
638 (mapconcat 'identity vc-path path-separator))
639 process-environment))
640 (w32-quote-process-args t))
641 (if (eq okstatus 'async)
642 (let ((proc (apply 'start-process command (current-buffer) command
643 squeezed)))
644 (message "Running %s in the background..." command)
645 ;;(set-process-sentinel proc (lambda (p msg) (delete-process p)))
646 (set-process-filter proc 'vc-process-filter)
647 (vc-exec-after
648 `(message "Running %s in the background... done" ',command)))
649 (setq status (apply 'call-process command nil t nil squeezed))
650 (when (or (not (integerp status)) (and okstatus (< okstatus status)))
651 (pop-to-buffer (current-buffer))
652 (goto-char (point-min))
653 (shrink-window-if-larger-than-buffer)
654 (error "Running %s...FAILED (%s)" command
655 (if (integerp status) (format "status %d" status) status))))
656 (if vc-command-messages
657 (message "Running %s...OK" command)))
658 (vc-exec-after
659 `(run-hook-with-args 'vc-post-command-functions ',command ',file ',flags))
660 status)))
594722a8 661
c4ae7096 662(defun vc-position-context (posn)
099bd78a
SM
663 "Save a bit of the text around POSN in the current buffer.
664Used to help us find the corresponding position again later
665if markers are destroyed or corrupted."
0e362f54
GM
666 ;; A lot of this was shamelessly lifted from Sebastian Kremer's
667 ;; rcs.el mode.
c4ae7096
JB
668 (list posn
669 (buffer-size)
670 (buffer-substring posn
671 (min (point-max) (+ posn 100)))))
672
c4ae7096 673(defun vc-find-position-by-context (context)
099bd78a 674 "Return the position of CONTEXT in the current buffer, or nil if not found."
c4ae7096
JB
675 (let ((context-string (nth 2 context)))
676 (if (equal "" context-string)
677 (point-max)
678 (save-excursion
679 (let ((diff (- (nth 1 context) (buffer-size))))
680 (if (< diff 0) (setq diff (- diff)))
681 (goto-char (nth 0 context))
682 (if (or (search-forward context-string nil t)
683 ;; Can't use search-backward since the match may continue
684 ;; after point.
685 (progn (goto-char (- (point) diff (length context-string)))
686 ;; goto-char doesn't signal an error at
687 ;; beginning of buffer like backward-char would
688 (search-forward context-string nil t)))
689 ;; to beginning of OSTRING
690 (- (point) (length context-string))))))))
691
4b398f5d 692(defun vc-context-matches-p (posn context)
099bd78a 693 "Return t if POSN matches CONTEXT, nil otherwise."
4b398f5d
AS
694 (let* ((context-string (nth 2 context))
695 (len (length context-string))
696 (end (+ posn len)))
697 (if (> end (1+ (buffer-size)))
698 nil
699 (string= context-string (buffer-substring posn end)))))
700
c8de1d91 701(defun vc-buffer-context ()
099bd78a
SM
702 "Return a list (POINT-CONTEXT MARK-CONTEXT REPARSE).
703Used by `vc-restore-buffer-context' to later restore the context."
c4ae7096 704 (let ((point-context (vc-position-context (point)))
cfadef63
RS
705 ;; Use mark-marker to avoid confusion in transient-mark-mode.
706 (mark-context (if (eq (marker-buffer (mark-marker)) (current-buffer))
707 (vc-position-context (mark-marker))))
708 ;; Make the right thing happen in transient-mark-mode.
ab877583
RM
709 (mark-active nil)
710 ;; We may want to reparse the compilation buffer after revert
711 (reparse (and (boundp 'compilation-error-list) ;compile loaded
712 (let ((curbuf (current-buffer)))
713 ;; Construct a list; each elt is nil or a buffer
714 ;; iff that buffer is a compilation output buffer
715 ;; that contains markers into the current buffer.
716 (save-excursion
0e362f54 717 (mapcar (lambda (buffer)
ab877583
RM
718 (set-buffer buffer)
719 (let ((errors (or
720 compilation-old-error-list
721 compilation-error-list))
722 (buffer-error-marked-p nil))
6fb6ab11 723 (while (and (consp errors)
ab877583 724 (not buffer-error-marked-p))
a1bda481 725 (and (markerp (cdr (car errors)))
e9c8e248
RM
726 (eq buffer
727 (marker-buffer
a1bda481 728 (cdr (car errors))))
e9c8e248 729 (setq buffer-error-marked-p t))
ab877583 730 (setq errors (cdr errors)))
0e362f54 731 (if buffer-error-marked-p buffer)))
ab877583 732 (buffer-list)))))))
c8de1d91
AS
733 (list point-context mark-context reparse)))
734
735(defun vc-restore-buffer-context (context)
0e362f54 736 "Restore point/mark, and reparse any affected compilation buffers.
099bd78a 737CONTEXT is that which `vc-buffer-context' returns."
c8de1d91
AS
738 (let ((point-context (nth 0 context))
739 (mark-context (nth 1 context))
740 (reparse (nth 2 context)))
ab877583
RM
741 ;; Reparse affected compilation buffers.
742 (while reparse
743 (if (car reparse)
0e362f54 744 (with-current-buffer (car reparse)
ab877583
RM
745 (let ((compilation-last-buffer (current-buffer)) ;select buffer
746 ;; Record the position in the compilation buffer of
747 ;; the last error next-error went to.
748 (error-pos (marker-position
749 (car (car-safe compilation-error-list)))))
750 ;; Reparse the error messages as far as they were parsed before.
751 (compile-reinitialize-errors '(4) compilation-parsing-end)
752 ;; Move the pointer up to find the error we were at before
753 ;; reparsing. Now next-error should properly go to the next one.
754 (while (and compilation-error-list
27f2f10b 755 (/= error-pos (car (car compilation-error-list))))
ab877583
RM
756 (setq compilation-error-list (cdr compilation-error-list))))))
757 (setq reparse (cdr reparse)))
e1f297e6 758
4b398f5d
AS
759 ;; if necessary, restore point and mark
760 (if (not (vc-context-matches-p (point) point-context))
761 (let ((new-point (vc-find-position-by-context point-context)))
762 (if new-point (goto-char new-point))))
01e02ab3
AS
763 (and mark-active
764 mark-context
765 (not (vc-context-matches-p (mark) mark-context))
766 (let ((new-mark (vc-find-position-by-context mark-context)))
767 (if new-mark (set-mark new-mark))))))
c4ae7096 768
c8de1d91 769(defun vc-revert-buffer1 (&optional arg no-confirm)
099bd78a
SM
770 "Revert buffer, trying to keep point and mark where user expects them.
771Tries to be clever in the face of changes due to expanded version control
772key words. This is important for typeahead to work as expected.
773ARG and NO-CONFIRM are passed on to `revert-buffer'."
c8de1d91
AS
774 (interactive "P")
775 (widen)
776 (let ((context (vc-buffer-context)))
4b398f5d
AS
777 ;; Use save-excursion here, because it may be able to restore point
778 ;; and mark properly even in cases where vc-restore-buffer-context
0e362f54 779 ;; would fail. However, save-excursion might also get it wrong --
4b398f5d
AS
780 ;; in this case, vc-restore-buffer-context gives it a second try.
781 (save-excursion
0e362f54 782 ;; t means don't call normal-mode;
4b398f5d
AS
783 ;; that's to preserve various minor modes.
784 (revert-buffer arg no-confirm t))
c8de1d91
AS
785 (vc-restore-buffer-context context)))
786
594722a8 787
97d3f950 788(defun vc-buffer-sync (&optional not-urgent)
099bd78a 789 "Make sure the current buffer and its working file are in sync.
0e362f54 790NOT-URGENT means it is ok to continue if the user says not to save."
bbf97570 791 (if (buffer-modified-p)
97d3f950
RS
792 (if (or vc-suppress-confirm
793 (y-or-n-p (format "Buffer %s modified; save it? " (buffer-name))))
794 (save-buffer)
0e362f54 795 (unless not-urgent
97d3f950
RS
796 (error "Aborted")))))
797
0e362f54 798(defun vc-workfile-unchanged-p (file)
099bd78a 799 "Has FILE changed since last checkout?"
594722a8 800 (let ((checkout-time (vc-file-getprop file 'vc-checkout-time))
0e362f54
GM
801 (lastmod (nth 5 (file-attributes file))))
802 (if checkout-time
803 (equal checkout-time lastmod)
804 (let ((unchanged (vc-call workfile-unchanged-p file)))
805 (vc-file-setprop file 'vc-checkout-time (if unchanged lastmod 0))
806 unchanged))))
46e33aee 807
0e362f54 808(defun vc-default-workfile-unchanged-p (file)
099bd78a 809 "Default check whether FILE is unchanged: diff against master version."
0e362f54 810 (zerop (vc-call diff file (vc-workfile-version file))))
594722a8 811
0e362f54
GM
812(defun vc-recompute-state (file)
813 "Force a recomputation of the version control state of FILE.
814The state is computed using the exact, and possibly expensive
815function `vc-BACKEND-state', not the heuristic."
816 (vc-file-setprop file 'vc-state (vc-call state file)))
e1f297e6 817
0e362f54
GM
818(defun vc-next-action-on-file (file verbose &optional comment)
819 "Do The Right Thing for a given version-controlled FILE.
820If COMMENT is specified, it will be used as an admin or checkin comment.
821If VERBOSE is non-nil, query the user rather than using default parameters."
822 (let ((visited (get-file-buffer file))
823 state version)
824 (when visited
825 ;; Check relation of buffer and file, and make sure
826 ;; user knows what he's doing. First, finding the file
827 ;; will check whether the file on disk is newer.
828 (if vc-dired-mode
829 (find-file-other-window file)
7849e179 830 (set-buffer (find-file-noselect file)))
0e362f54
GM
831 (if (not (verify-visited-file-modtime (current-buffer)))
832 (if (yes-or-no-p "Replace file on disk with buffer contents? ")
833 (write-file (buffer-file-name))
834 (error "Aborted"))
835 ;; Now, check if we have unsaved changes.
836 (vc-buffer-sync t)
837 (if (buffer-modified-p)
838 (or (y-or-n-p "Operate on disk file, keeping modified buffer? ")
839 (error "Aborted")))))
46e33aee 840
0e362f54 841 ;; Do the right thing
8989ab56 842 (if (not (vc-registered file))
0e362f54
GM
843 (vc-register verbose comment)
844 (vc-recompute-state file)
0ab66291 845 (if visited (vc-mode-line file))
0e362f54
GM
846 (setq state (vc-state file))
847 (cond
848 ;; up-to-date
849 ((or (eq state 'up-to-date)
850 (and verbose (eq state 'needs-patch)))
851 (cond
852 (verbose
853 ;; go to a different version
ceec5a0c 854 (setq version
1d502d5a 855 (read-string "Branch, version, or backend to move to: "))
ceec5a0c 856 (let ((vsym (intern-soft (upcase version))))
1d502d5a
AS
857 (if (member vsym vc-handled-backends)
858 (vc-transfer-file file vsym)
ceec5a0c 859 (vc-checkout file (eq (vc-checkout-model file) 'implicit)
1d502d5a 860 version))))
0e362f54
GM
861 ((not (eq (vc-checkout-model file) 'implicit))
862 ;; check the file out
863 (vc-checkout file t))
864 (t
865 ;; do nothing
866 (message "%s is up-to-date" file))))
46e33aee 867
0e362f54
GM
868 ;; Abnormal: edited but read-only
869 ((and visited (eq state 'edited) buffer-read-only)
870 ;; Make the file+buffer read-write. If the user really wanted to
871 ;; commit, he'll get a chance to do that next time around, anyway.
872 (message "File is edited but read-only; making it writable")
873 (set-file-modes buffer-file-name
874 (logior (file-modes buffer-file-name) 128))
875 (toggle-read-only -1))
46e33aee 876
0e362f54
GM
877 ;; edited
878 ((eq state 'edited)
6f41eeb5 879 (cond
0e362f54
GM
880 ;; For files with locking, if the file does not contain
881 ;; any changes, just let go of the lock, i.e. revert.
882 ((and (not (eq (vc-checkout-model file) 'implicit))
883 (vc-workfile-unchanged-p file)
884 ;; If buffer is modified, that means the user just
885 ;; said no to saving it; in that case, don't revert,
886 ;; because the user might intend to save after
887 ;; finishing the log entry.
888 (not (and visited (buffer-modified-p))))
889 ;; DO NOT revert the file without asking the user!
890 (if (not visited) (find-file-other-window file))
891 (if (yes-or-no-p "Revert to master version? ")
892 (vc-revert-buffer)))
893 (t ;; normal action
1d502d5a
AS
894 (if (not verbose)
895 (vc-checkin file nil comment)
896 (setq version (read-string "New version or backend: "))
897 (let ((vsym (intern (upcase version))))
898 (if (member vsym vc-handled-backends)
899 (vc-transfer-file file vsym)
900 (vc-checkin file version comment)))))))
46e33aee 901
0e362f54
GM
902 ;; locked by somebody else
903 ((stringp state)
904 (if comment
905 (error "Sorry, you can't steal the lock on %s this way"
906 (file-name-nondirectory file)))
907 (vc-steal-lock file
908 (if verbose (read-string "Version to steal: ")
909 (vc-workfile-version file))
910 state))
46e33aee 911
0e362f54
GM
912 ;; needs-patch
913 ((eq state 'needs-patch)
6f41eeb5 914 (if (yes-or-no-p (format
0e362f54
GM
915 "%s is not up-to-date. Get latest version? "
916 (file-name-nondirectory file)))
917 (vc-checkout file (eq (vc-checkout-model file) 'implicit) "")
918 (if (and (not (eq (vc-checkout-model file) 'implicit))
919 (yes-or-no-p "Lock this version? "))
920 (vc-checkout file t)
921 (error "Aborted"))))
46e33aee 922
0e362f54
GM
923 ;; needs-merge
924 ((eq state 'needs-merge)
6f41eeb5 925 (if (yes-or-no-p (format
0e362f54
GM
926 "%s is not up-to-date. Merge in changes now? "
927 (file-name-nondirectory file)))
928 (vc-maybe-resolve-conflicts file (vc-call merge-news file))
929 (error "Aborted")))
46e33aee 930
0e362f54
GM
931 ;; unlocked-changes
932 ((eq state 'unlocked-changes)
933 (if (not visited) (find-file-other-window file))
934 (if (save-window-excursion
935 (vc-version-diff file (vc-workfile-version file) nil)
936 (goto-char (point-min))
ceec5a0c
SM
937 (let ((inhibit-read-only t))
938 (insert-string
939 (format "Changes to %s since last lock:\n\n" file)))
0e362f54
GM
940 (not (beep))
941 (yes-or-no-p (concat "File has unlocked changes. "
942 "Claim lock retaining changes? ")))
943 (progn (vc-call steal-lock file)
944 ;; Must clear any headers here because they wouldn't
945 ;; show that the file is locked now.
946 (vc-clear-headers file)
947 (vc-mode-line file))
948 (if (not (yes-or-no-p
949 "Revert to checked-in version, instead? "))
950 (error "Checkout aborted")
951 (vc-revert-buffer1 t t)
952 (vc-checkout file t))))))))
e1f297e6 953
beba4bd9
AS
954(defvar vc-dired-window-configuration)
955
e1f297e6 956(defun vc-next-action-dired (file rev comment)
099bd78a
SM
957 "Call `vc-next-action-on-file' on all the marked files.
958Ignores FILE and REV, but passes on COMMENT."
3d30b8bc 959 (let ((dired-buffer (current-buffer))
8dd71345 960 (dired-dir default-directory))
632e9525 961 (dired-map-over-marks
3b574573 962 (let ((file (dired-get-filename)))
b0c9bc8c 963 (message "Processing %s..." file)
0e362f54
GM
964 (vc-next-action-on-file file nil comment)
965 (set-buffer dired-buffer)
3d30b8bc 966 (set-window-configuration vc-dired-window-configuration)
b0c9bc8c 967 (message "Processing %s...done" file))
3d30b8bc
RS
968 nil t))
969 (dired-move-to-filename))
e1f297e6 970
637a8ae9 971;; Here's the major entry point.
594722a8 972
637a8ae9 973;;;###autoload
594722a8
ER
974(defun vc-next-action (verbose)
975 "Do the next logical checkin or checkout operation on the current file.
0e362f54
GM
976
977If you call this from within a VC dired buffer with no files marked,
c6d4f628 978it will operate on the file in the current line.
0e362f54
GM
979
980If you call this from within a VC dired buffer, and one or more
c6d4f628
RS
981files are marked, it will accept a log message and then operate on
982each one. The log message will be used as a comment for any register
983or checkin operations, but ignored when doing checkouts. Attempted
984lock steals will raise an error.
0e362f54
GM
985
986A prefix argument lets you specify the version number to use.
80688f5c
RS
987
988For RCS and SCCS files:
594722a8 989 If the file is not already registered, this registers it for version
4b81132c 990control.
594722a8 991 If the file is registered and not locked by anyone, this checks out
34291cd2 992a writable and locked file ready for editing.
594722a8
ER
993 If the file is checked out and locked by the calling user, this
994first checks to see if the file has changed since checkout. If not,
995it performs a revert.
e1f297e6
ER
996 If the file has been changed, this pops up a buffer for entry
997of a log message; when the message has been entered, it checks in the
594722a8 998resulting changes along with the log message as change commentary. If
34291cd2 999the variable `vc-keep-workfiles' is non-nil (which is its default), a
594722a8
ER
1000read-only copy of the changed file is left in place afterwards.
1001 If the file is registered and locked by someone else, you are given
e1f297e6 1002the option to steal the lock.
80688f5c
RS
1003
1004For CVS files:
1005 If the file is not already registered, this registers it for version
1006control. This does a \"cvs add\", but no \"cvs commit\".
1007 If the file is added but not committed, it is committed.
80688f5c
RS
1008 If your working file is changed, but the repository file is
1009unchanged, this pops up a buffer for entry of a log message; when the
1010message has been entered, it checks in the resulting changes along
1011with the logmessage as change commentary. A writable file is retained.
1012 If the repository file is changed, you are asked if you want to
c6d4f628 1013merge in the changes into your working copy."
bbf97570 1014
594722a8 1015 (interactive "P")
8c0aaf40
ER
1016 (catch 'nogo
1017 (if vc-dired-mode
1018 (let ((files (dired-get-marked-files)))
3d30b8bc
RS
1019 (set (make-local-variable 'vc-dired-window-configuration)
1020 (current-window-configuration))
0e362f54 1021 (if (string= ""
8dd71345 1022 (mapconcat
0e362f54
GM
1023 (lambda (f)
1024 (if (not (vc-up-to-date-p f)) "@" ""))
b0c9bc8c
AS
1025 files ""))
1026 (vc-next-action-dired nil nil "dummy")
0ab66291 1027 (vc-start-entry nil nil nil nil
b0c9bc8c
AS
1028 "Enter a change comment for the marked files."
1029 'vc-next-action-dired))
8dd71345 1030 (throw 'nogo nil)))
dc08a6b5
AS
1031 (while vc-parent-buffer
1032 (pop-to-buffer vc-parent-buffer))
1033 (if buffer-file-name
1034 (vc-next-action-on-file buffer-file-name verbose)
1035 (error "Buffer %s is not associated with a file" (buffer-name)))))
594722a8
ER
1036
1037;;; These functions help the vc-next-action entry point
1038
637a8ae9 1039;;;###autoload
0e362f54 1040(defun vc-register (&optional set-version comment)
099bd78a 1041 "Register the current file into a version control system.
e1d95cc4 1042With prefix argument SET-VERSION, allow user to specify initial version
0e362f54
GM
1043level. If COMMENT is present, use that as an initial comment.
1044
e1d95cc4 1045The version control system to use is found by cycling through the list
0e362f54
GM
1046`vc-handled-backends'. The first backend in that list which declares
1047itself responsible for the file (usually because other files in that
1048directory are already registered under that backend) will be used to
1049register the file. If no backend declares itself responsible, the
1050first backend that could register the file is used."
594722a8 1051 (interactive "P")
099bd78a 1052 (unless buffer-file-name (error "No visited file"))
0e362f54
GM
1053 (when (vc-backend buffer-file-name)
1054 (if (vc-registered buffer-file-name)
1055 (error "This file is already registered")
1056 (unless (y-or-n-p "Previous master file has vanished. Make a new one? ")
1057 (error "Aborted"))))
02da6253
PE
1058 ;; Watch out for new buffers of size 0: the corresponding file
1059 ;; does not exist yet, even though buffer-modified-p is nil.
1060 (if (and (not (buffer-modified-p))
1061 (zerop (buffer-size))
1062 (not (file-exists-p buffer-file-name)))
1063 (set-buffer-modified-p t))
594722a8 1064 (vc-buffer-sync)
46e33aee 1065
0e362f54
GM
1066 (vc-start-entry buffer-file-name
1067 (if set-version
1d502d5a
AS
1068 (read-string (format "Initial version level for %s: "
1069 (buffer-name)))
0e362f54
GM
1070 ;; TODO: Use backend-specific init version.
1071 vc-default-init-version)
1072 (or comment (not vc-initial-comment))
0ab66291 1073 nil
0e362f54
GM
1074 "Enter initial comment."
1075 (lambda (file rev comment)
1076 (message "Registering %s... " file)
8989ab56 1077 (let ((backend (vc-responsible-backend file t)))
e1d95cc4 1078 (vc-file-clearprops file)
0e362f54
GM
1079 (vc-call-backend backend 'register file rev comment)
1080 (vc-file-setprop file 'vc-backend backend)
1081 (unless vc-make-backup-files
1082 (make-local-variable 'backup-inhibited)
1083 (setq backup-inhibited t)))
1084 (message "Registering %s... done" file))))
1085
8989ab56
AS
1086
1087(defun vc-responsible-backend (file &optional register)
1088 "Return the name of a backend system that is responsible for FILE.
46e33aee 1089The optional argument REGISTER means that a backend suitable for
8989ab56
AS
1090registration should be found.
1091
1092If REGISTER is nil, then if FILE is already registered, return the
1093backend of FILE. If FILE is not registered, or a directory, then the
1094first backend in `vc-handled-backends' that declares itself
1095responsible for FILE is returned. If no backend declares itself
1096responsible, return the first backend.
1097
1098If REGISTER is non-nil, return the first responsible backend under
1099which FILE is not yet registered. If there is no such backend, return
1100the first backend under which FILE is not yet registered, but could
1101be registered."
1102 (if (not vc-handled-backends)
1103 (error "No handled backends"))
1104 (or (and (not (file-directory-p file)) (not register) (vc-backend file))
1105 (catch 'found
1106 ;; First try: find a responsible backend. If this is for registration,
1107 ;; it must be a backend under which FILE is not yet registered.
1108 (dolist (backend vc-handled-backends)
1109 (and (or (not register)
1110 (not (vc-call-backend backend 'registered file)))
1111 (vc-call-backend backend 'responsible-p file)
1112 (throw 'found backend)))
1113 ;; no responsible backend
1114 (if (not register)
1115 ;; if this is not for registration, the first backend must do
1116 (car vc-handled-backends)
46e33aee 1117 ;; for registration, we need to find a new backend that
8989ab56
AS
1118 ;; could register FILE
1119 (dolist (backend vc-handled-backends)
1120 (and (not (vc-call-backend backend 'registered file))
1121 (vc-call-backend backend 'could-register file)
1122 (throw 'found backend)))
1123 (error "No backend that could register")))))
0e362f54 1124
099bd78a 1125(defun vc-default-responsible-p (backend file)
46e33aee 1126 "Indicate whether BACKEND is reponsible for FILE.
099bd78a
SM
1127The default is to return nil always."
1128 nil)
1129
0e362f54 1130(defun vc-default-could-register (backend file)
6f41eeb5 1131 "Return non-nil if BACKEND could be used to register FILE.
0e362f54
GM
1132The default implementation returns t for all files."
1133 t)
594722a8 1134
624b4662 1135(defun vc-resynch-window (file &optional keep noquery)
099bd78a
SM
1136 "If FILE is in the current buffer, either revert or unvisit it.
1137The choice between revert (to see expanded keywords) and unvisit depends on
1138`vc-keep-workfiles'. NOQUERY if non-nil inhibits confirmation for
0e362f54
GM
1139reverting. NOQUERY should be t *only* if it is known the only
1140difference between the buffer and the file is due to version control
1141rather than user editing!"
594722a8
ER
1142 (and (string= buffer-file-name file)
1143 (if keep
1144 (progn
1ab31687 1145 (vc-revert-buffer1 t noquery)
0e362f54
GM
1146 ;; TODO: Adjusting view mode might no longer be necessary
1147 ;; after RMS change to files.el of 1999-08-08. Investigate
1148 ;; this when we install the new VC.
f8791ebe
AS
1149 (and view-read-only
1150 (if (file-writable-p file)
1151 (and view-mode
1152 (let ((view-old-buffer-read-only nil))
1153 (view-mode-exit)))
1154 (and (not view-mode)
1155 (not (eq (get major-mode 'mode-class) 'special))
1156 (view-mode-enter))))
594722a8 1157 (vc-mode-line buffer-file-name))
88a2ffaf 1158 (kill-buffer (current-buffer)))))
594722a8 1159
503b5c85 1160(defun vc-resynch-buffer (file &optional keep noquery)
0e362f54 1161 "If FILE is currently visited, resynch its buffer."
4b398f5d
AS
1162 (if (string= buffer-file-name file)
1163 (vc-resynch-window file keep noquery)
1164 (let ((buffer (get-file-buffer file)))
1165 (if buffer
0e362f54
GM
1166 (with-current-buffer buffer
1167 (vc-resynch-window file keep noquery)))))
1168 (vc-dired-resynch-file file))
503b5c85 1169
0ab66291 1170(defun vc-start-entry (file rev comment initial-contents msg action &optional after-hook)
46e33aee 1171 "Accept a comment for an operation on FILE revision REV.
6f41eeb5 1172If COMMENT is nil, pop up a VC-log buffer, emit MSG, and set the
0ab66291
AS
1173action on close to ACTION. If COMMENT is a string and
1174INITIAL-CONTENTS is non-nil, then COMMENT is used as the initial
1175contents of the log entry buffer. If COMMENT is a string and
1176INITIAL-CONTENTS is nil, do action immediately as if the user had
1177entered COMMENT. If COMMENT is t, also do action immediately with an
46e33aee
TTN
1178empty comment. Remember the file's buffer in `vc-parent-buffer'
1179\(current one if no file). AFTER-HOOK specifies the local value
0ab66291
AS
1180for vc-log-operation-hook."
1181 (let ((parent (or (and file (get-file-buffer file)) (current-buffer))))
f0b188ed
RS
1182 (if vc-before-checkin-hook
1183 (if file
0e362f54 1184 (with-current-buffer parent
f0b188ed
RS
1185 (run-hooks 'vc-before-checkin-hook))
1186 (run-hooks 'vc-before-checkin-hook)))
0ab66291 1187 (if (and comment (not initial-contents))
e1f297e6
ER
1188 (set-buffer (get-buffer-create "*VC-log*"))
1189 (pop-to-buffer (get-buffer-create "*VC-log*")))
8c0aaf40
ER
1190 (set (make-local-variable 'vc-parent-buffer) parent)
1191 (set (make-local-variable 'vc-parent-buffer-name)
1192 (concat " from " (buffer-name vc-parent-buffer)))
7e869659 1193 (if file (vc-mode-line file))
099bd78a 1194 (vc-log-edit file)
b965445f
RS
1195 (make-local-variable 'vc-log-after-operation-hook)
1196 (if after-hook
1197 (setq vc-log-after-operation-hook after-hook))
e1f297e6 1198 (setq vc-log-operation action)
e1f297e6 1199 (setq vc-log-version rev)
0ab66291
AS
1200 (erase-buffer)
1201 (if (eq comment t)
1202 (vc-finish-logentry t)
1203 (if comment
1204 (insert comment))
1205 (if (and comment (not initial-contents))
1206 (vc-finish-logentry nil)
1207 (message "%s Type C-c C-c when done" msg)))))
e1f297e6 1208
c6d4f628 1209(defun vc-checkout (file &optional writable rev)
099bd78a
SM
1210 "Retrieve a copy of the revision REV of FILE.
1211If WRITABLE is non-nil, make sure the retrieved file is writable.
1212REV defaults to the latest revision."
ffda0460
AS
1213 (and writable
1214 (not rev)
1215 (vc-call make-version-backups file)
1216 (vc-up-to-date-p file)
1217 (copy-file file (vc-version-backup-file-name file)
1218 'ok-if-already-exists 'keep-date))
099bd78a
SM
1219 (with-vc-properties
1220 file
1221 (condition-case err
1222 (vc-call checkout file writable rev)
1223 (file-error
1224 ;; Maybe the backend is not installed ;-(
1225 (when writable
1226 (let ((buf (get-file-buffer file)))
1227 (when buf (with-current-buffer buf (toggle-read-only -1)))))
1228 (signal (car err) (cdr err))))
a3255400
SM
1229 `((vc-state . ,(if (or (eq (vc-checkout-model file) 'implicit)
1230 (not writable))
1231 (if (vc-call latest-on-branch-p file)
1232 'up-to-date
1233 'needs-patch)
1234 'edited))
1235 (vc-checkout-time . ,(nth 5 (file-attributes file)))))
b0c9bc8c 1236 (vc-resynch-buffer file t t))
594722a8 1237
0e362f54 1238(defun vc-steal-lock (file rev owner)
099bd78a 1239 "Steal the lock on FILE."
29fc1ce9 1240 (let (file-description)
29fc1ce9
RS
1241 (if rev
1242 (setq file-description (format "%s:%s" file rev))
1243 (setq file-description file))
4bc504c8
RS
1244 (if (not (yes-or-no-p (format "Steal the lock on %s from %s? "
1245 file-description owner)))
0e362f54
GM
1246 (error "Steal canceled"))
1247 (compose-mail owner (format "Stolen lock on %s" file-description)
1248 nil nil nil nil
1249 (list (list 'vc-finish-steal file rev)))
29fc1ce9 1250 (setq default-directory (expand-file-name "~/"))
29fc1ce9
RS
1251 (goto-char (point-max))
1252 (insert
1253 (format "I stole the lock on %s, " file-description)
1254 (current-time-string)
1255 ".\n")
1256 (message "Please explain why you stole the lock. Type C-c C-c when done.")))
594722a8
ER
1257
1258(defun vc-finish-steal (file version)
0e362f54
GM
1259 ;; This is called when the notification has been sent.
1260 (message "Stealing lock on %s..." file)
46e33aee 1261 (with-vc-properties
099bd78a
SM
1262 file
1263 (vc-call steal-lock file version)
a3255400 1264 `((vc-state . edited)))
0e362f54
GM
1265 (vc-resynch-buffer file t t)
1266 (message "Stealing lock on %s...done" file))
594722a8 1267
0ab66291 1268(defun vc-checkin (file &optional rev comment initial-contents)
6f41eeb5 1269 "Check in FILE.
0e362f54
GM
1270The optional argument REV may be a string specifying the new version
1271level (if nil increment the current level). COMMENT is a comment
0ab66291
AS
1272string; if omitted, a buffer is popped up to accept a comment. If
1273INITIAL-CONTENTS is non-nil, then COMMENT is used as the initial contents
1274of the log entry buffer.
0e362f54
GM
1275
1276If `vc-keep-workfiles' is nil, FILE is deleted afterwards, provided
1277that the version control system supports this mode of operation.
861f3c29
DL
1278
1279Runs the normal hook `vc-checkin-hook'."
6f41eeb5 1280 (vc-start-entry
0ab66291 1281 file rev comment initial-contents
6f41eeb5 1282 "Enter a change comment."
0e362f54
GM
1283 (lambda (file rev comment)
1284 (message "Checking in %s..." file)
1285 ;; "This log message intentionally left almost blank".
1286 ;; RCS 5.7 gripes about white-space-only comments too.
1287 (or (and comment (string-match "[^\t\n ]" comment))
1288 (setq comment "*** empty log message ***"))
46e33aee 1289 (with-vc-properties
099bd78a
SM
1290 file
1291 ;; Change buffers to get local value of vc-checkin-switches.
1292 (with-current-buffer (or (get-file-buffer file) (current-buffer))
ffda0460
AS
1293 (let ((backup-file (vc-version-backup-file file)))
1294 (vc-call checkin file rev comment)
1295 (if backup-file (delete-file backup-file))))
a3255400
SM
1296 `((vc-state . up-to-date)
1297 (vc-checkout-time . ,(nth 5 (file-attributes file)))
1298 (vc-workfile-version . nil)))
0e362f54
GM
1299 (message "Checking in %s...done" file))
1300 'vc-checkin-hook))
594722a8 1301
3b4dd9a9
RM
1302(defun vc-comment-to-change-log (&optional whoami file-name)
1303 "Enter last VC comment into change log file for current buffer's file.
1304Optional arg (interactive prefix) non-nil means prompt for user name and site.
1305Second arg is file name of change log. \
861f3c29
DL
1306If nil, uses `change-log-default-name'.
1307
1308May be useful as a `vc-checkin-hook' to update change logs automatically."
43cea1ab
RM
1309 (interactive (if current-prefix-arg
1310 (list current-prefix-arg
1311 (prompt-for-change-log-name))))
41208291
KH
1312 ;; Make sure the defvar for add-log-current-defun-function has been executed
1313 ;; before binding it.
1314 (require 'add-log)
3b4dd9a9
RM
1315 (let (;; Extract the comment first so we get any error before doing anything.
1316 (comment (ring-ref vc-comment-ring 0))
43cea1ab 1317 ;; Don't let add-change-log-entry insert a defun name.
3b4dd9a9
RM
1318 (add-log-current-defun-function 'ignore)
1319 end)
1320 ;; Call add-log to do half the work.
43cea1ab 1321 (add-change-log-entry whoami file-name t t)
3b4dd9a9
RM
1322 ;; Insert the VC comment, leaving point before it.
1323 (setq end (save-excursion (insert comment) (point-marker)))
1324 (if (looking-at "\\s *\\s(")
1325 ;; It starts with an open-paren, as in "(foo): Frobbed."
43cea1ab 1326 ;; So remove the ": " add-log inserted.
3b4dd9a9
RM
1327 (delete-char -2))
1328 ;; Canonicalize the white space between the file name and comment.
1329 (just-one-space)
1330 ;; Indent rest of the text the same way add-log indented the first line.
1331 (let ((indentation (current-indentation)))
1332 (save-excursion
1333 (while (< (point) end)
1334 (forward-line 1)
1335 (indent-to indentation))
c124b1b4 1336 (setq end (point))))
3b4dd9a9 1337 ;; Fill the inserted text, preserving open-parens at bol.
6b60c5d1
BG
1338 (let ((paragraph-separate (concat paragraph-separate "\\|\\s *\\s("))
1339 (paragraph-start (concat paragraph-start "\\|\\s *\\s(")))
43cea1ab 1340 (beginning-of-line)
c124b1b4
RM
1341 (fill-region (point) end))
1342 ;; Canonicalize the white space at the end of the entry so it is
1343 ;; separated from the next entry by a single blank line.
1344 (skip-syntax-forward " " end)
1345 (delete-char (- (skip-syntax-backward " ")))
1346 (or (eobp) (looking-at "\n\n")
1347 (insert "\n"))))
3b4dd9a9 1348
8c0aaf40 1349(defun vc-finish-logentry (&optional nocomment)
594722a8
ER
1350 "Complete the operation implied by the current log entry."
1351 (interactive)
8c0aaf40 1352 ;; Check and record the comment, if any.
0e362f54
GM
1353 (unless nocomment
1354 ;; Comment too long?
1355 (vc-call-backend (or (and vc-log-file (vc-backend vc-log-file))
1356 (vc-responsible-backend default-directory))
1357 'logentry-check)
1358 (run-hooks 'vc-logentry-check-hook)
1359 ;; Record the comment in the comment ring
1360 (let ((comment (buffer-string)))
1361 (unless (and (ring-p vc-comment-ring)
1362 (not (ring-empty-p vc-comment-ring))
1363 (equal comment (ring-ref vc-comment-ring 0)))
1364 (ring-insert vc-comment-ring comment))))
b2396d1f 1365 ;; Sync parent buffer in case the user modified it while editing the comment.
cdaf7a1a 1366 ;; But not if it is a vc-dired buffer.
0e362f54
GM
1367 (with-current-buffer vc-parent-buffer
1368 (or vc-dired-mode (vc-buffer-sync)))
61dee1e7
AS
1369 (if (not vc-log-operation) (error "No log operation is pending"))
1370 ;; save the parameters held in buffer-local variables
1371 (let ((log-operation vc-log-operation)
1372 (log-file vc-log-file)
1373 (log-version vc-log-version)
1374 (log-entry (buffer-string))
2c4eea90
KH
1375 (after-hook vc-log-after-operation-hook)
1376 (tmp-vc-parent-buffer vc-parent-buffer))
e2bef5c3 1377 (pop-to-buffer vc-parent-buffer)
61dee1e7
AS
1378 ;; OK, do it to it
1379 (save-excursion
0e362f54 1380 (funcall log-operation
61dee1e7
AS
1381 log-file
1382 log-version
1383 log-entry))
df1e7b91
KH
1384 ;; Remove checkin window (after the checkin so that if that fails
1385 ;; we don't zap the *VC-log* buffer and the typing therein).
1386 (let ((logbuf (get-buffer "*VC-log*")))
2c4eea90
KH
1387 (cond ((and logbuf vc-delete-logbuf-window)
1388 (delete-windows-on logbuf (selected-frame))
262c8cea 1389 ;; Kill buffer and delete any other dedicated windows/frames.
2c4eea90 1390 (kill-buffer logbuf))
0ab66291
AS
1391 (logbuf (pop-to-buffer "*VC-log*")
1392 (bury-buffer)
1393 (pop-to-buffer tmp-vc-parent-buffer))))
e2bef5c3 1394 ;; Now make sure we see the expanded headers
46e33aee 1395 (if log-file
0ab66291 1396 (vc-resynch-buffer log-file vc-keep-workfiles t))
0e362f54 1397 (if vc-dired-mode
0ab66291 1398 (dired-move-to-filename))
37667a5c 1399 (run-hooks after-hook 'vc-finish-logentry-hook)))
594722a8
ER
1400
1401;; Code for access to the comment ring
1402
0e362f54
GM
1403(defun vc-new-comment-index (stride len)
1404 (mod (cond
1405 (vc-comment-ring-index (+ vc-comment-ring-index stride))
1406 ;; Initialize the index on the first use of this command
1407 ;; so that the first M-p gets index 0, and the first M-n gets
1408 ;; index -1.
1409 ((> stride 0) (1- stride))
1410 (t stride))
1411 len))
1412
8c0aaf40
ER
1413(defun vc-previous-comment (arg)
1414 "Cycle backwards through comment history."
1415 (interactive "*p")
1416 (let ((len (ring-length vc-comment-ring)))
0e362f54
GM
1417 (if (<= len 0)
1418 (progn (message "Empty comment ring") (ding))
1419 (erase-buffer)
1420 (setq vc-comment-ring-index (vc-new-comment-index arg len))
1421 (message "Comment %d" (1+ vc-comment-ring-index))
1422 (insert (ring-ref vc-comment-ring vc-comment-ring-index)))))
8c0aaf40
ER
1423
1424(defun vc-next-comment (arg)
1425 "Cycle forwards through comment history."
1426 (interactive "*p")
1427 (vc-previous-comment (- arg)))
1428
0e362f54 1429(defun vc-comment-search-reverse (str &optional stride)
a3255400 1430 "Search backwards through comment history for substring match."
0e362f54
GM
1431 ;; Why substring rather than regexp ? -sm
1432 (interactive
1433 (list (read-string "Comment substring: " nil nil vc-last-comment-match)))
1434 (unless stride (setq stride 1))
8c0aaf40
ER
1435 (if (string= str "")
1436 (setq str vc-last-comment-match)
1437 (setq vc-last-comment-match str))
0e362f54
GM
1438 (let* ((str (regexp-quote str))
1439 (len (ring-length vc-comment-ring))
1440 (n (vc-new-comment-index stride len)))
1441 (while (progn (when (or (>= n len) (< n 0)) (error "Not found"))
1442 (not (string-match str (ring-ref vc-comment-ring n))))
1443 (setq n (+ n stride)))
1444 (setq vc-comment-ring-index n)
1445 (vc-previous-comment 0)))
8c0aaf40
ER
1446
1447(defun vc-comment-search-forward (str)
a3255400 1448 "Search forwards through comment history for substring match."
0e362f54
GM
1449 (interactive
1450 (list (read-string "Comment substring: " nil nil vc-last-comment-match)))
1451 (vc-comment-search-reverse str -1))
594722a8
ER
1452
1453;; Additional entry points for examining version histories
1454
637a8ae9 1455;;;###autoload
97d3f950 1456(defun vc-diff (historic &optional not-urgent)
e8ee1ccf 1457 "Display diffs between file versions.
0e362f54 1458Normally this compares the current file and buffer with the most recent
e8ee1ccf
RS
1459checked in version of that file. This uses no arguments.
1460With a prefix argument, it reads the file name to use
1461and two version designators specifying which versions to compare."
48078f8f 1462 (interactive (list current-prefix-arg t))
b6909007 1463 (vc-ensure-vc-buffer)
594722a8
ER
1464 (if historic
1465 (call-interactively 'vc-version-diff)
0e362f54 1466 (let ((file buffer-file-name))
c0d66cb2 1467 (vc-buffer-sync not-urgent)
0e362f54
GM
1468 (if (vc-workfile-unchanged-p buffer-file-name)
1469 (message "No changes to %s since latest version" file)
1470 (vc-version-diff file nil nil)))))
594722a8
ER
1471
1472(defun vc-version-diff (file rel1 rel2)
1473 "For FILE, report diffs between two stored versions REL1 and REL2 of it.
1474If FILE is a directory, generate diffs between versions for all registered
1475files in or below it."
0e362f54 1476 (interactive
4e6473c8
GM
1477 (let ((file (expand-file-name
1478 (read-file-name (if buffer-file-name
1479 "File or dir to diff: (default visited file) "
1480 "File or dir to diff: ")
1481 default-directory buffer-file-name t)))
c0d66cb2
RS
1482 (rel1-default nil) (rel2-default nil))
1483 ;; compute default versions based on the file state
1484 (cond
0e362f54
GM
1485 ;; if it's a directory, don't supply any version default
1486 ((file-directory-p file)
c0d66cb2 1487 nil)
0e362f54
GM
1488 ;; if the file is not up-to-date, use current version as older version
1489 ((not (vc-up-to-date-p file))
c0d66cb2
RS
1490 (setq rel1-default (vc-workfile-version file)))
1491 ;; if the file is not locked, use last and previous version as default
1492 (t
1493 (setq rel1-default (vc-previous-version (vc-workfile-version file)))
0e362f54 1494 (if (string= rel1-default "") (setq rel1-default nil))
c0d66cb2
RS
1495 (setq rel2-default (vc-workfile-version file))))
1496 ;; construct argument list
0e362f54 1497 (list file
8e710301
RS
1498 (read-string (if rel1-default
1499 (concat "Older version: (default "
1500 rel1-default ") ")
1501 "Older version: ")
1502 nil nil rel1-default)
1503 (read-string (if rel2-default
1504 (concat "Newer version: (default "
1505 rel2-default ") ")
ba27415c 1506 "Newer version (default: current source): ")
8e710301 1507 nil nil rel2-default))))
0e362f54 1508 (vc-setup-buffer "*vc-diff*")
594722a8 1509 (if (file-directory-p file)
ffda0460 1510 ;; recursive directory diff
0e362f54 1511 (let ((inhibit-read-only t))
ffda0460
AS
1512 (if (string-equal rel1 "") (setq rel1 nil))
1513 (if (string-equal rel2 "") (setq rel2 nil))
3234e2a3
ER
1514 (insert "Diffs between "
1515 (or rel1 "last version checked in")
1516 " and "
1517 (or rel2 "current workfile(s)")
1518 ":\n\n")
0e362f54
GM
1519 (setq default-directory (file-name-as-directory file))
1520 ;; FIXME: this should do a single exec in CVS.
594722a8 1521 (vc-file-tree-walk
2f119435 1522 default-directory
0e362f54
GM
1523 (lambda (f)
1524 (vc-exec-after
1525 `(progn
1526 (message "Looking at %s" ',f)
1527 (vc-call-backend ',(vc-backend file) 'diff ',f ',rel1 ',rel2)))))
1528 (vc-exec-after `(let ((inhibit-read-only t))
1529 (insert "\nEnd of diffs.\n"))))
ffda0460
AS
1530 ;; single file diff
1531 (if (or (not rel1) (string-equal rel1 ""))
1532 (setq rel1 (vc-workfile-version file)))
1533 (if (string-equal rel2 "")
1534 (setq rel2 nil))
1535 (let ((file-rel1 (vc-version-backup-file file rel1))
8f4f0214
SM
1536 (file-rel2 (if (not rel2)
1537 file
ffda0460
AS
1538 (vc-version-backup-file file rel2))))
1539 (if (and file-rel1 file-rel2)
8f4f0214
SM
1540 (apply 'vc-do-command t 1 "diff" nil
1541 (append (if (listp diff-switches)
1542 diff-switches
1543 (list diff-switches))
1544 (list (file-relative-name file-rel1)
1545 (file-relative-name file-rel2))))
ffda0460
AS
1546 (cd (file-name-directory file))
1547 (vc-call diff file rel1 rel2))))
0e362f54
GM
1548 (if (and (zerop (buffer-size))
1549 (not (get-buffer-process (current-buffer))))
1550 (progn
1551 (if rel1
1552 (if rel2
1553 (message "No changes to %s between %s and %s" file rel1 rel2)
1554 (message "No changes to %s since %s" file rel1))
1555 (message "No changes to %s since latest version" file))
1556 nil)
1557 (pop-to-buffer (current-buffer))
1558 ;; Gnus-5.8.5 sets up an autoload for diff-mode, even if it's
1559 ;; not available. Work around that.
1560 (if (require 'diff-mode nil t) (diff-mode))
1d502d5a
AS
1561 (vc-exec-after '(progn (if (eq (buffer-size) 0)
1562 (insert "No differences found.\n"))
1563 (goto-char (point-min))
0e362f54
GM
1564 (shrink-window-if-larger-than-buffer)))
1565 t))
594722a8 1566
f1818994
PE
1567;;;###autoload
1568(defun vc-version-other-window (rev)
1569 "Visit version REV of the current buffer in another window.
1570If the current buffer is named `F', the version is named `F.~REV~'.
1571If `F.~REV~' already exists, it is used instead of being re-created."
0e362f54 1572 (interactive "sVersion to visit (default is workfile version): ")
b6909007
AS
1573 (vc-ensure-vc-buffer)
1574 (let* ((version (if (string-equal rev "")
0e362f54 1575 (vc-workfile-version buffer-file-name)
b6909007
AS
1576 rev))
1577 (filename (concat buffer-file-name ".~" version "~")))
1578 (or (file-exists-p filename)
0e362f54 1579 (vc-call checkout buffer-file-name nil version filename))
b6909007 1580 (find-file-other-window filename)))
f1818994 1581
594722a8
ER
1582;; Header-insertion code
1583
637a8ae9 1584;;;###autoload
594722a8 1585(defun vc-insert-headers ()
099bd78a 1586 "Insert headers in a file for use with your version control system.
b524ce9f 1587Headers desired are inserted at point, and are pulled from
0e362f54 1588the variable `vc-BACKEND-header'."
594722a8 1589 (interactive)
b6909007 1590 (vc-ensure-vc-buffer)
594722a8
ER
1591 (save-excursion
1592 (save-restriction
1593 (widen)
1594 (if (or (not (vc-check-headers))
820bde8d 1595 (y-or-n-p "Version headers already exist. Insert another set? "))
594722a8
ER
1596 (progn
1597 (let* ((delims (cdr (assq major-mode vc-comment-alist)))
1598 (comment-start-vc (or (car delims) comment-start "#"))
1599 (comment-end-vc (or (car (cdr delims)) comment-end ""))
0e362f54
GM
1600 (hdsym (vc-make-backend-sym (vc-backend (buffer-file-name))
1601 'header))
1602 (hdstrings (and (boundp hdsym) (symbol-value hdsym))))
1603 (mapcar (lambda (s)
1604 (insert comment-start-vc "\t" s "\t"
1605 comment-end-vc "\n"))
594722a8
ER
1606 hdstrings)
1607 (if vc-static-header-alist
0e362f54
GM
1608 (mapcar (lambda (f)
1609 (if (string-match (car f) buffer-file-name)
1610 (insert (format (cdr f) (car hdstrings)))))
594722a8
ER
1611 vc-static-header-alist))
1612 )
1613 )))))
1614
0e362f54 1615(defun vc-clear-headers (&optional file)
099bd78a
SM
1616 "Clear all version headers in the current buffer (or FILE).
1617I.e. reset them to the non-expanded form."
0e362f54
GM
1618 (let* ((filename (or file buffer-file-name))
1619 (visited (find-buffer-visiting filename))
1620 (backend (vc-backend filename)))
1621 (when (vc-find-backend-function backend 'clear-headers)
6f41eeb5 1622 (if visited
0e362f54
GM
1623 (let ((context (vc-buffer-context)))
1624 ;; save-excursion may be able to relocate point and mark
1625 ;; properly. If it fails, vc-restore-buffer-context
1626 ;; will give it a second try.
1627 (save-excursion
1628 (vc-call-backend backend 'clear-headers))
1629 (vc-restore-buffer-context context))
7849e179 1630 (set-buffer (find-file-noselect filename))
0e362f54
GM
1631 (vc-call-backend backend 'clear-headers)
1632 (kill-buffer filename)))))
c8de1d91 1633
b6909007 1634;;;###autoload
099bd78a
SM
1635(defun vc-merge ()
1636 "Merge changes between two versions into the current buffer's file.
1637This asks for two versions to merge from in the minibuffer. If the
1638first version is a branch number, then merge all changes from that
1639branch. If the first version is empty, merge news, i.e. recent changes
1640from the current branch.
0e362f54
GM
1641
1642See Info node `Merging'."
099bd78a 1643 (interactive)
ccb141e8
AS
1644 (vc-ensure-vc-buffer)
1645 (vc-buffer-sync)
1646 (let* ((file buffer-file-name)
1647 (backend (vc-backend file))
0e362f54 1648 (state (vc-state file))
099bd78a 1649 first-version second-version status)
0e362f54 1650 (cond
0e362f54
GM
1651 ((stringp state)
1652 (error "File is locked by %s" state))
1653 ((not (vc-editable-p file))
1654 (if (y-or-n-p
1655 "File must be checked out for merging. Check out now? ")
1656 (vc-checkout file t)
1657 (error "Merge aborted"))))
46e33aee 1658 (setq first-version
099bd78a
SM
1659 (read-string (concat "Branch or version to merge from "
1660 "(default: news on current branch): ")))
1661 (if (string= first-version "")
1662 (if (not (vc-find-backend-function backend 'merge-news))
1663 (error "Sorry, merging news is not implemented for %s" backend)
1664 (setq status (vc-call merge-news file)))
1665 (if (not (vc-find-backend-function backend 'merge))
1666 (error "Sorry, merging is not implemented for %s" backend)
1667 (if (not (vc-branch-p first-version))
46e33aee
TTN
1668 (setq second-version
1669 (read-string "Second version: "
099bd78a
SM
1670 (concat (vc-branch-part first-version) ".")))
1671 ;; We want to merge an entire branch. Set versions
1672 ;; accordingly, so that vc-BACKEND-merge understands us.
1673 (setq second-version first-version)
1674 ;; first-version must be the starting point of the branch
1675 (setq first-version (vc-branch-part first-version)))
1676 (setq status (vc-call merge file first-version second-version))))
1677 (vc-maybe-resolve-conflicts file status "WORKFILE" "MERGE SOURCE")))
0e362f54
GM
1678
1679(defun vc-maybe-resolve-conflicts (file status &optional name-A name-B)
1680 (vc-resynch-buffer file t (not (buffer-modified-p)))
1681 (if (zerop status) (message "Merge successful")
1682 (if (fboundp 'smerge-mode) (smerge-mode 1))
1683 (if (y-or-n-p "Conflicts detected. Resolve them now? ")
1684 (if (fboundp 'smerge-ediff)
1685 (smerge-ediff)
1686 (vc-resolve-conflicts name-A name-B))
1687 (message "File contains conflict markers"))))
ccb141e8 1688
beba4bd9
AS
1689(defvar vc-ediff-windows)
1690(defvar vc-ediff-result)
0e362f54
GM
1691(eval-when-compile
1692 (defvar ediff-buffer-A)
1693 (defvar ediff-buffer-B)
1694 (defvar ediff-buffer-C)
1695 (require 'ediff-util))
ccb141e8
AS
1696;;;###autoload
1697(defun vc-resolve-conflicts (&optional name-A name-B)
18483cf0
AS
1698 "Invoke ediff to resolve conflicts in the current buffer.
1699The conflicts must be marked with rcsmerge conflict markers."
1700 (interactive)
b6909007 1701 (vc-ensure-vc-buffer)
18483cf0
AS
1702 (let* ((found nil)
1703 (file-name (file-name-nondirectory buffer-file-name))
0e362f54
GM
1704 (your-buffer (generate-new-buffer
1705 (concat "*" file-name
ccb141e8 1706 " " (or name-A "WORKFILE") "*")))
0e362f54
GM
1707 (other-buffer (generate-new-buffer
1708 (concat "*" file-name
ccb141e8 1709 " " (or name-B "CHECKED-IN") "*")))
18483cf0 1710 (result-buffer (current-buffer)))
0e362f54 1711 (save-excursion
18483cf0
AS
1712 (set-buffer your-buffer)
1713 (erase-buffer)
1714 (insert-buffer result-buffer)
1715 (goto-char (point-min))
0e362f54 1716 (while (re-search-forward (concat "^<<<<<<< "
18483cf0
AS
1717 (regexp-quote file-name) "\n") nil t)
1718 (setq found t)
1719 (replace-match "")
1720 (if (not (re-search-forward "^=======\n" nil t))
1721 (error "Malformed conflict marker"))
1722 (replace-match "")
1723 (let ((start (point)))
1724 (if (not (re-search-forward "^>>>>>>> [0-9.]+\n" nil t))
1725 (error "Malformed conflict marker"))
1726 (delete-region start (point))))
1727 (if (not found)
1728 (progn
1729 (kill-buffer your-buffer)
1730 (kill-buffer other-buffer)
1731 (error "No conflict markers found")))
1732 (set-buffer other-buffer)
1733 (erase-buffer)
1734 (insert-buffer result-buffer)
1735 (goto-char (point-min))
0e362f54 1736 (while (re-search-forward (concat "^<<<<<<< "
18483cf0
AS
1737 (regexp-quote file-name) "\n") nil t)
1738 (let ((start (match-beginning 0)))
1739 (if (not (re-search-forward "^=======\n" nil t))
1740 (error "Malformed conflict marker"))
1741 (delete-region start (point))
1742 (if (not (re-search-forward "^>>>>>>> [0-9.]+\n" nil t))
1743 (error "Malformed conflict marker"))
1744 (replace-match "")))
1745 (let ((config (current-window-configuration))
1746 (ediff-default-variant 'default-B))
1747
1748 ;; Fire up ediff.
1749
1750 (set-buffer (ediff-merge-buffers your-buffer other-buffer))
1751
1752 ;; Ediff is now set up, and we are in the control buffer.
1753 ;; Do a few further adjustments and take precautions for exit.
1754
1755 (make-local-variable 'vc-ediff-windows)
1756 (setq vc-ediff-windows config)
1757 (make-local-variable 'vc-ediff-result)
0e362f54 1758 (setq vc-ediff-result result-buffer)
18483cf0 1759 (make-local-variable 'ediff-quit-hook)
6f41eeb5 1760 (setq ediff-quit-hook
0e362f54
GM
1761 (lambda ()
1762 (let ((buffer-A ediff-buffer-A)
1763 (buffer-B ediff-buffer-B)
1764 (buffer-C ediff-buffer-C)
1765 (result vc-ediff-result)
1766 (windows vc-ediff-windows))
1767 (ediff-cleanup-mess)
1768 (set-buffer result)
1769 (erase-buffer)
1770 (insert-buffer buffer-C)
1771 (kill-buffer buffer-A)
1772 (kill-buffer buffer-B)
1773 (kill-buffer buffer-C)
1774 (set-window-configuration windows)
1775 (message "Conflict resolution finished; you may save the buffer"))))
18483cf0
AS
1776 (message "Please resolve conflicts now; exit ediff when done")
1777 nil))))
1778
2f119435 1779;; The VC directory major mode. Coopt Dired for this.
e1f297e6
ER
1780;; All VC commands get mapped into logical equivalents.
1781
beba4bd9
AS
1782(defvar vc-dired-switches)
1783(defvar vc-dired-terse-mode)
1784
0e362f54
GM
1785(defvar vc-dired-mode-map
1786 (let ((map (make-sparse-keymap))
1787 (vmap (make-sparse-keymap)))
0e362f54 1788 (define-key map "\C-xv" vc-prefix-map)
099bd78a
SM
1789 ;; Emacs-20 has a lousy keymap inheritance that won't work here.
1790 ;; Emacs-21's is still lousy but just better enough that it'd work. -sm
1791 ;; (set-keymap-parent vmap vc-prefix-map)
1792 (setq vmap vc-prefix-map)
0e362f54 1793 (define-key map "v" vmap)
0e362f54
GM
1794 (define-key vmap "t" 'vc-dired-toggle-terse-mode)
1795 map))
1796
2f119435 1797(define-derived-mode vc-dired-mode dired-mode "Dired under VC"
0e362f54
GM
1798 "The major mode used in VC directory buffers.
1799
1800It works like Dired, but lists only files under version control, with
1801the current VC state of each file being indicated in the place of the
1802file's link count, owner, group and size. Subdirectories are also
1803listed, and you may insert them into the buffer as desired, like in
1804Dired.
1805
1806All Dired commands operate normally, with the exception of `v', which
1807is redefined as the version control prefix, so that you can type
3d30b8bc
RS
1808`vl', `v=' etc. to invoke `vc-print-log', `vc-diff', and the like on
1809the file named in the current Dired buffer line. `vv' invokes
1810`vc-next-action' on this file, or on all files currently marked.
1811There is a special command, `*l', to mark all files currently locked."
099bd78a
SM
1812 ;; define-derived-mode does it for us in Emacs-21, but not in Emacs-20.
1813 ;; We do it here because dired might not be loaded yet
1814 ;; when vc-dired-mode-map is initialized.
1815 (set-keymap-parent vc-dired-mode-map dired-mode-map)
421f0bfe
AS
1816 (make-local-hook 'dired-after-readin-hook)
1817 (add-hook 'dired-after-readin-hook 'vc-dired-hook nil t)
edcb979f
AS
1818 ;; The following is slightly modified from dired.el,
1819 ;; because file lines look a bit different in vc-dired-mode.
1820 (set (make-local-variable 'dired-move-to-filename-regexp)
0e362f54 1821 (let*
edcb979f
AS
1822 ((l "\\([A-Za-z]\\|[^\0-\177]\\)")
1823 ;; In some locales, month abbreviations are as short as 2 letters,
1824 ;; and they can be padded on the right with spaces.
1825 (month (concat l l "+ *"))
0e362f54 1826 ;; Recognize any non-ASCII character.
edcb979f
AS
1827 ;; The purpose is to match a Kanji character.
1828 (k "[^\0-\177]")
1829 ;; (k "[^\x00-\x7f\x80-\xff]")
1830 (s " ")
1831 (yyyy "[0-9][0-9][0-9][0-9]")
1832 (mm "[ 0-1][0-9]")
1833 (dd "[ 0-3][0-9]")
1834 (HH:MM "[ 0-2][0-9]:[0-5][0-9]")
1835 (western (concat "\\(" month s dd "\\|" dd s month "\\)"
61d6c25d 1836 s "\\(" HH:MM "\\|" s yyyy"\\|" yyyy s "\\)"))
edcb979f 1837 (japanese (concat mm k s dd k s "\\(" s HH:MM "\\|" yyyy k "\\)")))
0e362f54
GM
1838 ;; the .* below ensures that we find the last match on a line
1839 (concat ".*" s "\\(" western "\\|" japanese "\\)" s)))
a0019b45
AS
1840 (and (boundp 'vc-dired-switches)
1841 vc-dired-switches
1842 (set (make-local-variable 'dired-actual-switches)
1843 vc-dired-switches))
3b574573 1844 (set (make-local-variable 'vc-dired-terse-mode) vc-dired-terse-display)
2f119435
AS
1845 (setq vc-dired-mode t))
1846
3b574573
AS
1847(defun vc-dired-toggle-terse-mode ()
1848 "Toggle terse display in VC Dired."
1849 (interactive)
1850 (if (not vc-dired-mode)
1851 nil
1852 (setq vc-dired-terse-mode (not vc-dired-terse-mode))
1853 (if vc-dired-terse-mode
1854 (vc-dired-hook)
1855 (revert-buffer))))
1856
3d30b8bc
RS
1857(defun vc-dired-mark-locked ()
1858 "Mark all files currently locked."
1859 (interactive)
1860 (dired-mark-if (let ((f (dired-get-filename nil t)))
1861 (and f
1862 (not (file-directory-p f))
0e362f54 1863 (not (vc-up-to-date-p f))))
3d30b8bc
RS
1864 "locked file"))
1865
1866(define-key vc-dired-mode-map "*l" 'vc-dired-mark-locked)
1867
0e362f54
GM
1868(defun vc-default-dired-state-info (backend file)
1869 (let ((state (vc-state file)))
1870 (cond
1871 ((stringp state) (concat "(" state ")"))
1872 ((eq state 'edited) (concat "(" (vc-user-login-name) ")"))
1873 ((eq state 'needs-merge) "(merge)")
1874 ((eq state 'needs-patch) "(patch)")
1875 ((eq state 'unlocked-changes) "(stale)"))))
b0c9bc8c 1876
8c0aaf40 1877(defun vc-dired-reformat-line (x)
0e362f54
GM
1878 "Reformat a directory-listing line.
1879Replace various columns with version control information.
1880This code, like dired, assumes UNIX -l format."
3d30b8bc 1881 (beginning-of-line)
edcb979f 1882 (let ((pos (point)) limit perm date-and-file)
2f119435
AS
1883 (end-of-line)
1884 (setq limit (point))
1885 (goto-char pos)
edcb979f
AS
1886 (when
1887 (or
1888 (re-search-forward ;; owner and group
1889 "^\\(..[drwxlts-]+ \\) *[0-9]+ [^ ]+ +[^ ]+ +[0-9]+\\( .*\\)"
0e362f54 1890 limit t)
edcb979f 1891 (re-search-forward ;; only owner displayed
0e362f54 1892 "^\\(..[drwxlts-]+ \\) *[0-9]+ [^ ]+ +[0-9]+\\( .*\\)"
edcb979f
AS
1893 limit t)
1894 (re-search-forward ;; OS/2 -l format, no links, owner, group
1895 "^\\(..[drwxlts-]+ \\) *[0-9]+\\( .*\\)"
1896 limit t))
2f119435 1897 (setq perm (match-string 1)
edcb979f
AS
1898 date-and-file (match-string 2))
1899 (setq x (substring (concat x " ") 0 10))
1900 (replace-match (concat perm x date-and-file)))))
3d30b8bc
RS
1901
1902(defun vc-dired-hook ()
0e362f54
GM
1903 "Reformat the listing according to version control.
1904Called by dired after any portion of a vc-dired buffer has been read in."
3d30b8bc 1905 (message "Getting version information... ")
eccceb78 1906 (let (subdir filename (buffer-read-only nil) cvs-dir)
3d30b8bc 1907 (goto-char (point-min))
0e362f54
GM
1908 (while (not (eobp))
1909 (cond
3d30b8bc
RS
1910 ;; subdir header line
1911 ((setq subdir (dired-get-subdir))
0e362f54
GM
1912 ;; if the backend supports it, get the state
1913 ;; of all files in this directory at once
1914 (let ((backend (vc-responsible-backend subdir)))
1915 (if (vc-find-backend-function backend 'dir-state)
1916 (vc-call-backend backend 'dir-state subdir)))
3d30b8bc
RS
1917 (forward-line 1)
1918 ;; erase (but don't remove) the "total" line
0e362f54
GM
1919 (delete-region (point) (line-end-position))
1920 (beginning-of-line)
1921 (forward-line 1))
1922 ;; file line
3d30b8bc
RS
1923 ((setq filename (dired-get-filename nil t))
1924 (cond
3b574573 1925 ;; subdir
3d30b8bc 1926 ((file-directory-p filename)
0e362f54
GM
1927 (cond
1928 ((member (file-name-nondirectory filename)
3b574573
AS
1929 vc-directory-exclusion-list)
1930 (let ((pos (point)))
1931 (dired-kill-tree filename)
1932 (goto-char pos)
1933 (dired-kill-line)))
1934 (vc-dired-terse-mode
633cee46
AS
1935 ;; Don't show directories in terse mode. Don't use
1936 ;; dired-kill-line to remove it, because in recursive listings,
1937 ;; that would remove the directory contents as well.
0e362f54 1938 (delete-region (line-beginning-position)
633cee46 1939 (progn (forward-line 1) (point))))
3b574573
AS
1940 ((string-match "\\`\\.\\.?\\'" (file-name-nondirectory filename))
1941 (dired-kill-line))
1942 (t
3d30b8bc 1943 (vc-dired-reformat-line nil)
3b574573
AS
1944 (forward-line 1))))
1945 ;; ordinary file
0e362f54
GM
1946 ((and (vc-backend filename)
1947 (not (and vc-dired-terse-mode
1948 (vc-up-to-date-p filename))))
1949 (vc-dired-reformat-line (vc-call dired-state-info filename))
3d30b8bc 1950 (forward-line 1))
0e362f54 1951 (t
3d30b8bc
RS
1952 (dired-kill-line))))
1953 ;; any other line
3b574573
AS
1954 (t (forward-line 1))))
1955 (vc-dired-purge))
1956 (message "Getting version information... done")
1957 (save-restriction
1958 (widen)
633cee46
AS
1959 (cond ((eq (count-lines (point-min) (point-max)) 1)
1960 (goto-char (point-min))
1961 (message "No files locked under %s" default-directory)))))
3b574573
AS
1962
1963(defun vc-dired-purge ()
0e362f54 1964 "Remove empty subdirs."
3b574573
AS
1965 (let (subdir)
1966 (goto-char (point-min))
1967 (while (setq subdir (dired-get-subdir))
1968 (forward-line 2)
1969 (if (dired-get-filename nil t)
1970 (if (not (dired-next-subdir 1 t))
1971 (goto-char (point-max)))
1972 (forward-line -2)
1973 (if (not (string= (dired-current-directory) default-directory))
1974 (dired-do-kill-lines t "")
633cee46
AS
1975 ;; We cannot remove the top level directory.
1976 ;; Just make it look a little nicer.
1977 (forward-line 1)
1978 (kill-line)
3b574573
AS
1979 (if (not (dired-next-subdir 1 t))
1980 (goto-char (point-max))))))
1981 (goto-char (point-min))))
2f119435 1982
0e362f54
GM
1983(defun vc-dired-buffers-for-dir (dir)
1984 "Return a list of all vc-dired buffers that currently display DIR."
1985 (let (result)
099bd78a
SM
1986 ;; Check whether dired is loaded.
1987 (when (fboundp 'dired-buffers-for-dir)
1988 (mapcar (lambda (buffer)
1989 (with-current-buffer buffer
1990 (if vc-dired-mode
1991 (setq result (append result (list buffer))))))
1992 (dired-buffers-for-dir dir)))
0e362f54
GM
1993 result))
1994
1995(defun vc-dired-resynch-file (file)
1996 "Update the entries for FILE in any VC Dired buffers that list it."
1997 (let ((buffers (vc-dired-buffers-for-dir (file-name-directory file))))
1998 (when buffers
1999 (mapcar (lambda (buffer)
2000 (with-current-buffer buffer
2001 (if (dired-goto-file file)
2002 ;; bind vc-dired-terse-mode to nil so that
2003 ;; files won't vanish when they are checked in
2004 (let ((vc-dired-terse-mode nil))
2005 (dired-do-redisplay 1)))))
2006 buffers))))
2007
637a8ae9 2008;;;###autoload
0e362f54
GM
2009(defun vc-directory (dir read-switches)
2010 "Create a buffer in VC Dired Mode for directory DIR.
2011
2012See Info node `VC Dired Mode'.
2013
2014With prefix arg READ-SWITCHES, specify a value to override
2015`dired-listing-switches' when generating the listing."
2f119435 2016 (interactive "DDired under VC (directory): \nP")
0e362f54 2017 (let ((vc-dired-switches (concat vc-dired-listing-switches
3b574573 2018 (if vc-dired-recurse "R" ""))))
0e362f54 2019 (if read-switches
3b574573
AS
2020 (setq vc-dired-switches
2021 (read-string "Dired listing switches: "
2022 vc-dired-switches)))
3d30b8bc
RS
2023 (require 'dired)
2024 (require 'dired-aux)
0e362f54
GM
2025 (switch-to-buffer
2026 (dired-internal-noselect (expand-file-name (file-name-as-directory dir))
2027 vc-dired-switches
3d30b8bc 2028 'vc-dired-mode))))
e70bdc98 2029
594722a8
ER
2030
2031;; Named-configuration entry points
2032
0e362f54 2033(defun vc-snapshot-precondition (dir)
099bd78a
SM
2034 "Scan the tree below DIR, looking for non-uptodate files.
2035If any file is not up-to-date, return the name of the first such file.
2036\(This means, neither snapshot creation nor retrieval is allowed.\)
2037If one or more of the files are currently visited, return `visited'.
2038Otherwise, return nil."
503b5c85
RS
2039 (let ((status nil))
2040 (catch 'vc-locked-example
2041 (vc-file-tree-walk
0e362f54
GM
2042 dir
2043 (lambda (f)
2044 (if (not (vc-up-to-date-p f)) (throw 'vc-locked-example f)
2045 (if (get-file-buffer f) (setq status 'visited)))))
503b5c85 2046 status)))
594722a8 2047
637a8ae9 2048;;;###autoload
0e362f54 2049(defun vc-create-snapshot (dir name branchp)
6f41eeb5 2050 "Descending recursively from DIR, make a snapshot called NAME.
0e362f54
GM
2051For each registered file, the version level of its latest version
2052becomes part of the named configuration. If the prefix argument
2053BRANCHP is given, the snapshot is made as a new branch and the files
2054are checked out in that new branch."
2055 (interactive
2056 (list (read-file-name "Directory: " default-directory default-directory t)
2057 (read-string "New snapshot name: ")
2058 current-prefix-arg))
2059 (message "Making %s... " (if branchp "branch" "snapshot"))
2060 (if (file-directory-p dir) (setq dir (file-name-as-directory dir)))
2061 (vc-call-backend (vc-responsible-backend dir)
2062 'create-snapshot dir name branchp)
2063 (message "Making %s... done" (if branchp "branch" "snapshot")))
2064
2065(defun vc-default-create-snapshot (backend dir name branchp)
6f41eeb5 2066 (when branchp
0e362f54
GM
2067 (error "VC backend %s does not support module branches" backend))
2068 (let ((result (vc-snapshot-precondition dir)))
503b5c85 2069 (if (stringp result)
0e362f54 2070 (error "File %s is not up-to-date" result)
1dabb4e6 2071 (vc-file-tree-walk
0e362f54
GM
2072 dir
2073 (lambda (f)
2074 (vc-call assign-name f name))))))
594722a8 2075
637a8ae9 2076;;;###autoload
0e362f54 2077(defun vc-retrieve-snapshot (dir name)
099bd78a
SM
2078 "Descending recursively from DIR, retrieve the snapshot called NAME.
2079If NAME is empty, it refers to the latest versions.
2080If locking is used for the files in DIR, then there must not be any
2081locked files at or below DIR (but if NAME is empty, locked files are
2082allowed and simply skipped)."
0e362f54
GM
2083 (interactive
2084 (list (read-file-name "Directory: " default-directory default-directory t)
2085 (read-string "Snapshot name to retrieve (default latest versions): ")))
2086 (let ((update (yes-or-no-p "Update any affected buffers? "))
2087 (msg (if (or (not name) (string= name ""))
2088 (format "Updating %s... " (abbreviate-file-name dir))
2089 (format "Retrieving snapshot into %s... "
2090 (abbreviate-file-name dir)))))
2091 (message msg)
2092 (vc-call-backend (vc-responsible-backend dir)
2093 'retrieve-snapshot dir name update)
2094 (message (concat msg "done"))))
2095
2096(defun vc-default-retrieve-snapshot (backend dir name update)
2097 (if (string= name "")
2098 (progn
2099 (vc-file-tree-walk
2100 dir
2101 (lambda (f) (and
2102 (vc-up-to-date-p f)
2103 (vc-error-occurred
2104 (vc-call checkout f nil "")
2105 (if update (vc-resynch-buffer f t t)))))))
2106 (let ((result (vc-snapshot-precondition dir)))
2107 (if (stringp result)
2108 (error "File %s is locked" result)
2109 (setq update (and (eq result 'visited) update))
2110 (vc-file-tree-walk
2111 dir
2112 (lambda (f) (and
2113 (vc-error-occurred
2114 (vc-call checkout f nil name)
2115 (if update (vc-resynch-buffer f t t))))))))))
594722a8
ER
2116
2117;; Miscellaneous other entry points
2118
637a8ae9 2119;;;###autoload
594722a8
ER
2120(defun vc-print-log ()
2121 "List the change log of the current buffer in a window."
2122 (interactive)
b6909007
AS
2123 (vc-ensure-vc-buffer)
2124 (let ((file buffer-file-name))
0e362f54 2125 (vc-setup-buffer nil)
b6909007 2126 (setq default-directory (file-name-directory file))
0e362f54
GM
2127 (vc-call print-log file)
2128 (pop-to-buffer (current-buffer))
2129 (if (fboundp 'log-view-mode) (log-view-mode))
2130 (vc-exec-after
2131 `(progn
2132 (goto-char (point-max)) (forward-line -1)
2133 (while (looking-at "=*\n")
2134 (delete-char (- (match-end 0) (match-beginning 0)))
2135 (forward-line -1))
2136 (goto-char (point-min))
2137 (if (looking-at "[\b\t\n\v\f\r ]+")
2138 (delete-char (- (match-end 0) (match-beginning 0))))
2139 (shrink-window-if-larger-than-buffer)
2140 ;; move point to the log entry for the current version
2141 (if (fboundp 'log-view-goto-rev)
2142 (log-view-goto-rev ',(vc-workfile-version file))
2143 (if (vc-find-backend-function ',(vc-backend file) 'show-log-entry)
6f41eeb5
DL
2144 (vc-call-backend ',(vc-backend file)
2145 'show-log-entry
0e362f54 2146 ',(vc-workfile-version file))))))))
594722a8 2147
0ab66291
AS
2148(defun vc-default-comment-history (backend file)
2149 "Return a string with all log entries that were made under BACKEND for FILE."
2150 (if (vc-find-backend-function backend 'print-log)
2151 (with-temp-buffer
2152 (vc-call print-log file)
2153 (vc-call wash-log file)
2154 (buffer-string))))
2155
2156(defun vc-default-wash-log (backend file)
2157 "Remove all non-comment information from log output.
2158This default implementation works for RCS logs; backends should override
2159it if their logs are not in RCS format."
2160 (let ((separator (concat "^-+\nrevision [0-9.]+\ndate: .*\n"
2161 "\\(branches: .*;\n\\)?"
2162 "\\(\\*\\*\\* empty log message \\*\\*\\*\n\\)?")))
2163 (goto-char (point-max)) (forward-line -1)
2164 (while (looking-at "=*\n")
2165 (delete-char (- (match-end 0) (match-beginning 0)))
2166 (forward-line -1))
2167 (goto-char (point-min))
2168 (if (looking-at "[\b\t\n\v\f\r ]+")
2169 (delete-char (- (match-end 0) (match-beginning 0))))
2170 (goto-char (point-min))
2171 (re-search-forward separator nil t)
2172 (delete-region (point-min) (point))
2173 (while (re-search-forward separator nil t)
2174 (delete-region (match-beginning 0) (match-end 0)))))
2175
637a8ae9 2176;;;###autoload
594722a8 2177(defun vc-revert-buffer ()
18483cf0 2178 "Revert the current buffer's file back to the version it was based on.
9c95ac44 2179This asks for confirmation if the buffer contents are not identical
7849e179
SM
2180to that version. This function does not automatically pick up newer
2181changes found in the master file; use \\[universal-argument] \\[vc-next-action] to do so."
594722a8 2182 (interactive)
b6909007 2183 (vc-ensure-vc-buffer)
594722a8 2184 (let ((file buffer-file-name)
221cc4f4
RS
2185 ;; This operation should always ask for confirmation.
2186 (vc-suppress-confirm nil)
ffda0460
AS
2187 (obuf (current-buffer))
2188 status)
0e362f54 2189 (unless (vc-workfile-unchanged-p file)
a3255400
SM
2190 ;; vc-diff selects the new window, which is not what we want:
2191 ;; if the new window is on another frame, that'd require the user
2192 ;; moving her mouse to answer the yes-or-no-p question.
2193 (let ((win (save-selected-window
2194 (setq status (vc-diff nil t)) (selected-window))))
2195 (vc-exec-after `(message nil))
2196 (when status
2197 (unwind-protect
2198 (unless (yes-or-no-p "Discard changes? ")
ffda0460 2199 (error "Revert canceled"))
a3255400
SM
2200 (select-window win)
2201 (if (one-window-p t)
2202 (if (window-dedicated-p (selected-window))
2203 (make-frame-invisible))
2204 (delete-window))))))
751fa747 2205 (set-buffer obuf)
0e362f54
GM
2206 ;; Do the reverting
2207 (message "Reverting %s..." file)
045e1aa5 2208 (vc-revert-file file)
0e362f54 2209 (message "Reverting %s...done" file)))
594722a8 2210
ffda0460
AS
2211(defun vc-version-backup-file (file &optional rev)
2212 "If version backups should be used for FILE, and there exists
2213such a backup for REV or the current workfile version of file,
2214return the name of it; otherwise return nil."
2215 (when (vc-call make-version-backups file)
2216 (let ((backup-file (vc-version-backup-file-name file rev)))
2217 (and (file-exists-p backup-file)
2218 backup-file))))
2219
045e1aa5
AS
2220(defun vc-revert-file (file)
2221 "Revert FILE back to the version it was based on."
045e1aa5
AS
2222 (with-vc-properties
2223 file
ffda0460
AS
2224 (let ((backup-file (vc-version-backup-file file)))
2225 (if (not backup-file)
2226 (vc-call revert file)
2227 (copy-file backup-file file 'ok-if-already-exists 'keep-date)
2228 (delete-file backup-file)))
a3255400
SM
2229 `((vc-state . up-to-date)
2230 (vc-checkout-time . ,(nth 5 (file-attributes file)))))
045e1aa5
AS
2231 (vc-resynch-buffer file t t))
2232
637a8ae9 2233;;;###autoload
594722a8 2234(defun vc-cancel-version (norevert)
34291cd2 2235 "Get rid of most recently checked in version of this file.
099bd78a 2236A prefix argument NOREVERT means do not revert the buffer afterwards."
594722a8 2237 (interactive "P")
b6909007 2238 (vc-ensure-vc-buffer)
099bd78a
SM
2239 (let* ((file (buffer-file-name))
2240 (backend (vc-backend file))
2241 (target (vc-workfile-version file))
7e48e092 2242 (config (current-window-configuration)) done)
0e362f54 2243 (cond
099bd78a 2244 ((not (vc-find-backend-function backend 'cancel-version))
0e362f54 2245 (error "Sorry, canceling versions is not supported under %s" backend))
099bd78a 2246 ((not (vc-call latest-on-branch-p file))
0e362f54 2247 (error "This is not the latest version; VC cannot cancel it"))
099bd78a 2248 ((not (vc-up-to-date-p file))
0e362f54 2249 (error (substitute-command-keys "File is not up to date; use \\[vc-revert-buffer] to discard changes"))))
7e48e092 2250 (if (null (yes-or-no-p (format "Remove version %s from master? " target)))
099bd78a 2251 (error "Aborted")
0e362f54
GM
2252 (setq norevert (or norevert (not
2253 (yes-or-no-p "Revert buffer to most recent remaining version? "))))
2254
099bd78a
SM
2255 (message "Removing last change from %s..." file)
2256 (with-vc-properties
2257 file
2258 (vc-call cancel-version file norevert)
a3255400 2259 `((vc-state . ,(if norevert 'edited 'up-to-date))
46e33aee
TTN
2260 (vc-checkout-time . ,(if norevert
2261 0
099bd78a 2262 (nth 5 (file-attributes file))))
a3255400 2263 (vc-workfile-version . nil)))
099bd78a
SM
2264 (message "Removing last change from %s...done" file)
2265
2266 (cond
2267 (norevert ;; clear version headers and mark the buffer modified
2268 (set-visited-file-name file)
2269 (when (not vc-make-backup-files)
2270 ;; inhibit backup for this buffer
2271 (make-local-variable 'backup-inhibited)
2272 (setq backup-inhibited t))
2273 (setq buffer-read-only nil)
2274 (vc-clear-headers)
2275 (vc-mode-line file)
2276 (vc-dired-resynch-file file))
2277 (t ;; revert buffer to file on disk
2278 (vc-resynch-buffer file t t)))
0e362f54
GM
2279 (message "Version %s has been removed from the master" target))))
2280
1d502d5a
AS
2281;;;autoload
2282(defun vc-switch-backend (file backend)
7849e179 2283 "Make BACKEND the current version control system for FILE.
1d502d5a
AS
2284FILE must already be registered in BACKEND. The change is not
2285permanent, only for the current session. This function only changes
7849e179
SM
2286VC's perspective on FILE, it does not register or unregister it.
2287By default, this command cycles through the registered backends.
2288To get a prompt, use a prefix argument."
2289 (interactive
1d502d5a
AS
2290 (list
2291 buffer-file-name
7849e179
SM
2292 (let ((backend (vc-backend buffer-file-name))
2293 (backends nil))
2294 ;; Find the registered backends.
2295 (dolist (backend vc-handled-backends)
2296 (when (vc-call-backend backend 'registered buffer-file-name)
2297 (push backend backends)))
2298 ;; Find the next backend.
ceec5a0c 2299 (let ((def (car (delq backend (append (memq backend backends) backends))))
7849e179
SM
2300 (others (delete backend backends)))
2301 (cond
2302 ((null others) (error "No other backend to switch to"))
2303 (current-prefix-arg
2304 (intern
2305 (upcase
2306 (completing-read
2307 (format "Switch to backend [%s]: " def)
2308 (mapcar (lambda (b) (list (downcase (symbol-name b)))) backends)
2309 nil t nil nil (downcase (symbol-name def))))))
2310 (t def))))))
ceec5a0c 2311 (unless (eq backend (vc-backend file))
ceec5a0c
SM
2312 (vc-file-clearprops file)
2313 (vc-file-setprop file 'vc-backend backend)
2314 ;; Force recomputation of the state
a3255400
SM
2315 (unless (vc-call-backend backend 'registered file)
2316 (vc-file-clearprops file)
2317 (error "%s is not registered in %s" file backend))
ceec5a0c 2318 (vc-mode-line file)))
1d502d5a 2319
1d502d5a
AS
2320;;;autoload
2321(defun vc-transfer-file (file new-backend)
ceec5a0c 2322 "Transfer FILE to another version control system NEW-BACKEND.
1d502d5a 2323If NEW-BACKEND has a higher precedence than FILE's current backend
ceec5a0c 2324\(i.e. it comes earlier in `vc-handled-backends'), then register FILE in
1d502d5a
AS
2325NEW-BACKEND, using the version number from the current backend as the
2326base level. If NEW-BACKEND has a lower precedence than the current
2327backend, then commit all changes that were made under the current
2328backend to NEW-BACKEND, and unregister FILE from the current backend.
2329\(If FILE is not yet registered under NEW-BACKEND, register it.)"
72cfc5fb
AS
2330 (let* ((old-backend (vc-backend file))
2331 (edited (memq (vc-state file) '(edited needs-merge)))
2332 (registered (vc-call-backend new-backend 'registered file))
2333 (move
2334 (and registered ; Never move if not registered in new-backend yet.
2335 ;; move if new-backend comes later in vc-handled-backends
2336 (or (memq new-backend (memq old-backend vc-handled-backends))
ffda0460 2337 (y-or-n-p "Final transfer? "))))
72cfc5fb 2338 (comment nil))
1d502d5a 2339 (if (eq old-backend new-backend)
72cfc5fb
AS
2340 (error "%s is the current backend of %s" new-backend file))
2341 (if registered
2342 (set-file-modes file (logior (file-modes file) 128))
2343 ;; `registered' might have switched under us.
2344 (vc-switch-backend file old-backend)
ffda0460
AS
2345 (let* ((rev (vc-workfile-version file))
2346 (modified-file (and edited (make-temp-name file)))
2347 (unmodified-file (and modified-file (vc-version-backup-file file))))
72cfc5fb
AS
2348 ;; Go back to the base unmodified file.
2349 (unwind-protect
2350 (progn
ffda0460
AS
2351 (when modified-file
2352 (copy-file file modified-file)
2353 ;; If we have a local copy of the unmodified file, handle that
2354 ;; here and not in vc-revert-file because we don't want to
2355 ;; delete that copy -- it is still useful for OLD-BACKEND.
2356 (if unmodified-file
2357 (copy-file unmodified-file file 'ok-if-already-exists)
2358 (if (y-or-n-p "Get base version from master? ")
2359 (vc-revert-file file))))
72cfc5fb 2360 (vc-call-backend new-backend 'receive-file file rev))
ffda0460 2361 (when modified-file
72cfc5fb
AS
2362 (vc-switch-backend file new-backend)
2363 (unless (eq (vc-checkout-model file) 'implicit)
2364 (vc-checkout file t nil))
ffda0460
AS
2365 (rename-file modified-file file 'ok-if-already-exists)
2366 (vc-file-setprop file 'vc-checkout-time nil)))))
72cfc5fb
AS
2367 (when move
2368 (vc-switch-backend file old-backend)
2369 (setq comment (vc-call comment-history file))
2370 (vc-call unregister file))
2371 (vc-switch-backend file new-backend)
2372 (when (or move edited)
1d502d5a 2373 (vc-file-setprop file 'vc-state 'edited)
ffda0460 2374 (vc-mode-line file)
0ab66291 2375 (vc-checkin file nil comment (stringp comment)))))
1d502d5a 2376
72cfc5fb
AS
2377(defun vc-default-unregister (backend file)
2378 "Default implementation of `vc-unregister', signals an error."
2379 (error "Unregistering files is not supported for %s" backend))
2380
2381(defun vc-default-receive-file (backend file rev)
2382 "Let BACKEND receive FILE from another version control system."
2383 (vc-call-backend backend 'register file rev ""))
2384
0e362f54
GM
2385(defun vc-rename-master (oldmaster newfile templates)
2386 "Rename OLDMASTER to be the master file for NEWFILE based on TEMPLATES."
2387 (let* ((dir (file-name-directory (expand-file-name oldmaster)))
2388 (newdir (or (file-name-directory newfile) ""))
2389 (newbase (file-name-nondirectory newfile))
2390 (masters
2391 ;; List of potential master files for `newfile'
2392 (mapcar
2393 (lambda (s) (vc-possible-master s newdir newbase))
2394 templates)))
2395 (if (or (file-symlink-p oldmaster)
2396 (file-symlink-p (file-name-directory oldmaster)))
2397 (error "This unsafe in the presence of symbolic links"))
2398 (rename-file
2399 oldmaster
2400 (catch 'found
2401 ;; If possible, keep the master file in the same directory.
2402 (mapcar (lambda (f)
2403 (if (and f (string= (file-name-directory (expand-file-name f))
2404 dir))
2405 (throw 'found f)))
2406 masters)
2407 ;; If not, just use the first possible place.
2408 (mapcar (lambda (f)
2409 (and f
2410 (or (not (setq dir (file-name-directory f)))
2411 (file-directory-p dir))
2412 (throw 'found f)))
2413 masters)
2414 (error "New file lacks a version control directory")))))
594722a8 2415
29fc1ce9 2416;;;###autoload
594722a8 2417(defun vc-rename-file (old new)
34291cd2
RS
2418 "Rename file OLD to NEW, and rename its master file likewise."
2419 (interactive "fVC rename file: \nFRename to: ")
80688f5c
RS
2420 ;; There are several ways of renaming files under CVS 1.3, but they all
2421 ;; have serious disadvantages. See the FAQ (available from think.com in
2422 ;; pub/cvs/). I'd rather send the user an error, than do something he might
2423 ;; consider to be wrong. When the famous, long-awaited rename database is
2424 ;; implemented things might change for the better. This is unlikely to occur
2425 ;; until CVS 2.0 is released. --ceder 1994-01-23 21:27:51
0e362f54
GM
2426 (let ((oldbuf (get-file-buffer old))
2427 (backend (vc-backend old)))
2428 (unless (or (null backend) (vc-find-backend-function backend 'rename-file))
2429 (error "Renaming files under %s is not supported in VC" backend))
d52f0de9 2430 (if (and oldbuf (buffer-modified-p oldbuf))
590cc449 2431 (error "Please save files before moving them"))
594722a8 2432 (if (get-file-buffer new)
590cc449 2433 (error "Already editing new file name"))
d52f0de9
RS
2434 (if (file-exists-p new)
2435 (error "New file already exists"))
0e362f54
GM
2436 (when backend
2437 (if (and backend (not (vc-up-to-date-p old)))
2438 (error "Please check in files before moving them"))
2439 (vc-call-backend backend 'rename-file old new))
2440 ;; Move the actual file (unless the backend did it already)
2441 (if (or (not backend) (file-exists-p old))
2442 (rename-file old new))
2443 ;; ?? Renaming a file might change its contents due to keyword expansion.
2444 ;; We should really check out a new copy if the old copy was precisely equal
2445 ;; to some checked in version. However, testing for this is tricky....
594722a8 2446 (if oldbuf
0e362f54 2447 (with-current-buffer oldbuf
4c145b9e
RS
2448 (let ((buffer-read-only buffer-read-only))
2449 (set-visited-file-name new))
2450 (vc-backend new)
2451 (vc-mode-line new)
0e362f54
GM
2452 (set-buffer-modified-p nil)))))
2453
2454;; Only defined in very recent Emacsen
2455(defvar small-temporary-file-directory nil)
594722a8 2456
637a8ae9 2457;;;###autoload
f35ecf88 2458(defun vc-update-change-log (&rest args)
0e362f54 2459 "Find change log file and add entries from recent version control logs.
d68e6990 2460Normally, find log entries for all registered files in the default
0e362f54 2461directory.
d68e6990 2462
099bd78a 2463With prefix arg of \\[universal-argument], only find log entries for the current buffer's file.
d68e6990
RS
2464
2465With any numeric prefix arg, find log entries for all currently visited
2466files that are under version control. This puts all the entries in the
2467log for the default directory, which may not be appropriate.
2468
099bd78a 2469From a program, any ARGS are assumed to be filenames for which
0e362f54 2470log entries should be gathered."
67242a23
RM
2471 (interactive
2472 (cond ((consp current-prefix-arg) ;C-u
2473 (list buffer-file-name))
2474 (current-prefix-arg ;Numeric argument.
2475 (let ((files nil)
2476 (buffers (buffer-list))
2477 file)
2478 (while buffers
2479 (setq file (buffer-file-name (car buffers)))
f3c61d82 2480 (and file (vc-backend file)
4b40fdea 2481 (setq files (cons file files)))
67242a23 2482 (setq buffers (cdr buffers)))
4b40fdea
PE
2483 files))
2484 (t
0e362f54
GM
2485 ;; Don't supply any filenames to backend; this means
2486 ;; it should find all relevant files relative to
2487 ;; the default-directory.
73a9679c 2488 nil)))
0e362f54
GM
2489 (vc-call-backend (vc-responsible-backend default-directory)
2490 'update-changelog args))
2491
2492(defun vc-default-update-changelog (backend files)
099bd78a
SM
2493 "Default implementation of update-changelog.
2494Uses `rcs2log' which only works for RCS and CVS."
0e362f54 2495 ;; FIXME: We (c|sh)ould add support for cvs2cl
449decf5 2496 (let ((odefault default-directory)
124c852b
RS
2497 (changelog (find-change-log))
2498 ;; Presumably not portable to non-Unixy systems, along with rcs2log:
0e362f54
GM
2499 (tempfile (funcall
2500 (if (fboundp 'make-temp-file) 'make-temp-file 'make-temp-name)
57c298c4
EZ
2501 (expand-file-name "vc"
2502 (or small-temporary-file-directory
2503 temporary-file-directory))))
b91916f3 2504 (full-name (or add-log-full-name
8172cd86
AS
2505 (user-full-name)
2506 (user-login-name)
2507 (format "uid%d" (number-to-string (user-uid)))))
b91916f3
RS
2508 (mailing-address (or add-log-mailing-address
2509 user-mail-address)))
124c852b 2510 (find-file-other-window changelog)
41dfb835
RS
2511 (barf-if-buffer-read-only)
2512 (vc-buffer-sync)
2513 (undo-boundary)
2514 (goto-char (point-min))
2515 (push-mark)
2516 (message "Computing change log entries...")
4b40fdea 2517 (message "Computing change log entries... %s"
124c852b
RS
2518 (unwind-protect
2519 (progn
0e362f54 2520 (setq default-directory odefault)
6f41eeb5
DL
2521 (if (eq 0 (apply 'call-process
2522 (expand-file-name "rcs2log"
2523 exec-directory)
0e362f54
GM
2524 nil (list t tempfile) nil
2525 "-c" changelog
2526 "-u" (concat (vc-user-login-name)
2527 "\t" full-name
2528 "\t" mailing-address)
2529 (mapcar
2530 (lambda (f)
2531 (file-relative-name
2532 (if (file-name-absolute-p f)
2533 f
2534 (concat odefault f))))
2535 files)))
2536 "done"
124c852b
RS
2537 (pop-to-buffer
2538 (set-buffer (get-buffer-create "*vc*")))
2539 (erase-buffer)
2540 (insert-file tempfile)
2541 "failed"))
0e362f54 2542 (setq default-directory (file-name-directory changelog))
124c852b 2543 (delete-file tempfile)))))
7d2d9482 2544
0e362f54 2545;;; Annotate functionality
7d2d9482 2546
f80f7bc2
RS
2547;; Declare globally instead of additional parameter to
2548;; temp-buffer-show-function (not possible to pass more than one
2549;; parameter).
099bd78a
SM
2550(defvar vc-annotate-ratio nil "Global variable.")
2551(defvar vc-annotate-backend nil "Global variable.")
0e362f54
GM
2552
2553(defun vc-annotate-get-backend (buffer)
099bd78a
SM
2554 "Return the backend matching \"Annotate\" buffer BUFFER.
2555Return NIL if no match made. Associations are made based on
0e362f54
GM
2556`vc-annotate-buffers'."
2557 (cdr (assoc buffer vc-annotate-buffers)))
7d2d9482 2558
0e362f54
GM
2559(define-derived-mode vc-annotate-mode fundamental-mode "Annotate"
2560 "Major mode for buffers displaying output from the `annotate' command.
7d2d9482
RS
2561
2562You can use the mode-specific menu to alter the time-span of the used
2563colors. See variable `vc-annotate-menu-elements' for customizing the
2564menu items."
7d2d9482
RS
2565 (vc-annotate-add-menu))
2566
2567(defun vc-annotate-display-default (&optional event)
2568 "Use the default color spectrum for VC Annotate mode."
0e362f54 2569 (interactive "e")
f80f7bc2 2570 (message "Redisplaying annotation...")
0e362f54 2571 (vc-annotate-display (current-buffer)
6f41eeb5 2572 nil
0e362f54 2573 (vc-annotate-get-backend (current-buffer)))
f80f7bc2 2574 (message "Redisplaying annotation...done"))
7d2d9482
RS
2575
2576(defun vc-annotate-add-menu ()
0e362f54
GM
2577 "Add the menu 'Annotate' to the menu bar in VC-Annotate mode."
2578 (setq vc-annotate-mode-menu (make-sparse-keymap "Annotate"))
2579 (define-key vc-annotate-mode-map [menu-bar vc-annotate-mode]
2580 (cons "VC-Annotate" vc-annotate-mode-menu))
7d2d9482
RS
2581 (define-key vc-annotate-mode-menu [default]
2582 '("Default" . vc-annotate-display-default))
2583 (let ((menu-elements vc-annotate-menu-elements))
2584 (while menu-elements
2585 (let* ((element (car menu-elements))
0e362f54
GM
2586 (days (round (* element
2587 (vc-annotate-car-last-cons vc-annotate-color-map)
7d2d9482
RS
2588 0.7585))))
2589 (setq menu-elements (cdr menu-elements))
2590 (define-key vc-annotate-mode-menu
2591 (vector days)
2592 (cons (format "Span %d days"
2593 days)
2594 `(lambda ()
2595 ,(format "Use colors spanning %d days" days)
f80f7bc2
RS
2596 (interactive)
2597 (message "Redisplaying annotation...")
2598 (vc-annotate-display
2599 (get-buffer (buffer-name))
0e362f54
GM
2600 (vc-annotate-time-span vc-annotate-color-map ,element)
2601 (vc-annotate-get-backend (current-buffer)))
f80f7bc2 2602 (message "Redisplaying annotation...done"))))))))
594722a8 2603
0e362f54
GM
2604
2605;;;; (defun vc-BACKEND-annotate-command (file buffer) ...)
2606;;;; Execute "annotate" on FILE by using `call-process' and insert
2607;;;; the contents in BUFFER.
2608
7d2d9482
RS
2609;;;###autoload
2610(defun vc-annotate (ratio)
0e362f54
GM
2611 "Display the result of the \"Annotate\" command using colors.
2612\"Annotate\" is defined by `vc-BACKEND-annotate-command'. New lines
2613are displayed in red, old in blue. A prefix argument specifies a
2614factor for stretching the time scale.
7d2d9482
RS
2615
2616`vc-annotate-menu-elements' customizes the menu elements of the
2617mode-specific menu. `vc-annotate-color-map' and
2618`vc-annotate-very-old-color' defines the mapping of time to
2619colors. `vc-annotate-background' specifies the background color."
2620 (interactive "p")
b6909007 2621 (vc-ensure-vc-buffer)
7d2d9482 2622 (message "Annotating...")
0e362f54 2623 (let ((temp-buffer-name (concat "*Annotate " (buffer-name) "*"))
7d2d9482 2624 (temp-buffer-show-function 'vc-annotate-display)
0e362f54
GM
2625 (vc-annotate-ratio ratio)
2626 (vc-annotate-backend (vc-backend (buffer-file-name))))
099bd78a
SM
2627 (if (not (vc-find-backend-function vc-annotate-backend 'annotate-command))
2628 (error "Sorry, annotating is not implemented for %s"
2629 vc-annotate-backend))
46e33aee 2630 (with-output-to-temp-buffer temp-buffer-name
0e362f54
GM
2631 (vc-call-backend vc-annotate-backend 'annotate-command
2632 (file-name-nondirectory (buffer-file-name))
2633 (get-buffer temp-buffer-name)))
2634 ;; Don't use the temp-buffer-name until the buffer is created
2635 ;; (only after `with-output-to-temp-buffer'.)
6f41eeb5 2636 (setq vc-annotate-buffers
0e362f54
GM
2637 (append vc-annotate-buffers
2638 (list (cons (get-buffer temp-buffer-name) vc-annotate-backend)))))
7d2d9482
RS
2639 (message "Annotating... done"))
2640
0e362f54 2641
f70419a8
RS
2642(defun vc-annotate-car-last-cons (a-list)
2643 "Return car of last cons in association list A-LIST."
2644 (if (not (eq nil (cdr a-list)))
2645 (vc-annotate-car-last-cons (cdr a-list))
2646 (car (car a-list))))
2647
2648(defun vc-annotate-time-span (a-list span &optional quantize)
6f41eeb5 2649 "Apply factor SPAN to the time-span of association list A-LIST.
0e362f54
GM
2650Return the new alist.
2651Optionally quantize to the factor of QUANTIZE."
7d2d9482 2652 ;; Apply span to each car of every cons
0e362f54 2653 (if (not (eq nil a-list))
f70419a8
RS
2654 (append (list (cons (* (car (car a-list)) span)
2655 (cdr (car a-list))))
0e362f54
GM
2656 (vc-annotate-time-span (nthcdr (or quantize ; optional
2657 1) ; Default to cdr
f70419a8
RS
2658 a-list) span quantize))))
2659
2660(defun vc-annotate-compcar (threshold a-list)
0e362f54
GM
2661 "Test successive cons cells of association list A-LIST against THRESHOLD.
2662Return the first cons cell which car is not less than THRESHOLD,
2663nil otherwise"
f70419a8
RS
2664 (let ((i 1)
2665 (tmp-cons (car a-list)))
2666 (while (and tmp-cons (< (car tmp-cons) threshold))
2667 (setq tmp-cons (car (nthcdr i a-list)))
2668 (setq i (+ i 1)))
2669 tmp-cons)) ; Return the appropriate value
2670
7d2d9482 2671
0e362f54
GM
2672;;;; (defun vc-BACKEND-annotate-difference (point) ...)
2673;;;;
2674;;;; Return the difference between the age of the line at point and
2675;;;; the current time. Return NIL if there is no more comparison to
2676;;;; be made in the buffer. Return value as defined for
2677;;;; `current-time'. You can safely assume that point is placed at
2678;;;; the beginning of each line, starting at `point-min'. The buffer
2679;;;; that point is placed in is the Annotate output, as defined by
2680;;;; the relevant backend.
2681
2682(defun vc-annotate-display (buffer &optional color-map backend)
099bd78a
SM
2683 "Do the VC-Annotate display in BUFFER using COLOR-MAP.
2684The original annotating file is supposed to be handled by BACKEND.
2685If BACKEND is NIL, variable VC-ANNOTATE-BACKEND is used instead.
2686This function is destructive on VC-ANNOTATE-BACKEND when BACKEND is non-nil."
7d2d9482 2687
f80f7bc2
RS
2688 ;; Handle the case of the global variable vc-annotate-ratio being
2689 ;; set. This variable is used to pass information from function
2690 ;; vc-annotate since it is not possible to use another parameter
0e362f54 2691 ;; (see temp-buffer-show-function).
7d2d9482 2692 (if (and (not color-map) vc-annotate-ratio)
f80f7bc2
RS
2693 ;; This will only be true if called from vc-annotate with ratio
2694 ;; being non-nil.
2695 (setq color-map (vc-annotate-time-span vc-annotate-color-map
2696 vc-annotate-ratio)))
0e362f54
GM
2697 (set-buffer buffer)
2698 (display-buffer buffer)
2699 (if (not vc-annotate-mode) ; Turn on vc-annotate-mode if not done
2700 (vc-annotate-mode))
2701 (goto-char (point-min)) ; Position at the top of the buffer.
2702 ;; Delete old overlays
2703 (mapcar
2704 (lambda (overlay)
2705 (if (overlay-get overlay 'vc-annotation)
2706 (delete-overlay overlay)))
2707 (overlays-in (point-min) (point-max)))
2708 (goto-char (point-min)) ; Position at the top of the buffer.
2709
2710 (if backend (setq vc-annotate-backend backend)) ; Destructive on `vc-annotate-backend'
2711
2712 (let ((difference (vc-call-backend vc-annotate-backend 'annotate-difference (point))))
2713 (while difference
2714 (let*
2715 ((color (or (vc-annotate-compcar
2716 difference (or color-map vc-annotate-color-map))
2717 (cons nil vc-annotate-very-old-color)))
2718 ;; substring from index 1 to remove any leading `#' in the name
2719 (face-name (concat "vc-annotate-face-" (substring (cdr color) 1)))
2720 ;; Make the face if not done.
2721 (face (or (intern-soft face-name)
2722 (let ((tmp-face (make-face (intern face-name))))
2723 (set-face-foreground tmp-face (cdr color))
2724 (if vc-annotate-background
2725 (set-face-background tmp-face vc-annotate-background))
2726 tmp-face))) ; Return the face
2727 (point (point))
2728 overlay)
f70419a8 2729 (forward-line 1)
05dad1e6
AS
2730 (setq overlay (make-overlay point (point)))
2731 (overlay-put overlay 'face face)
0e362f54
GM
2732 (overlay-put overlay 'vc-annotation t))
2733 (setq difference (vc-call-backend vc-annotate-backend 'annotate-difference (point))))))
f70419a8 2734
7d2d9482 2735\f
c6d4f628 2736;; Collect back-end-dependent stuff here
594722a8 2737
0e362f54 2738(defalias 'vc-default-logentry-check 'ignore)
594722a8 2739
594722a8
ER
2740(defun vc-check-headers ()
2741 "Check if the current file has any headers in it."
2742 (interactive)
0e362f54 2743 (vc-call-backend (vc-backend buffer-file-name) 'check-headers))
594722a8
ER
2744
2745;; Back-end-dependent stuff ends here.
2746
2747;; Set up key bindings for use while editing log messages
2748
099bd78a 2749(define-derived-mode vc-log-mode text-mode "VC-Log"
0e362f54 2750 "Major mode for editing VC log entries.
594722a8
ER
2751These bindings are added to the global keymap when you enter this mode:
2752\\[vc-next-action] perform next logical version-control operation on current file
0e362f54 2753\\[vc-register] register current file
594722a8
ER
2754\\[vc-toggle-read-only] like next-action, but won't register files
2755\\[vc-insert-headers] insert version-control headers in current file
2756\\[vc-print-log] display change history of current file
2757\\[vc-revert-buffer] revert buffer to latest version
2758\\[vc-cancel-version] undo latest checkin
2759\\[vc-diff] show diffs between file versions
f1818994 2760\\[vc-version-other-window] visit old version in another window
594722a8 2761\\[vc-directory] show all files locked by any user in or below .
0e362f54 2762\\[vc-annotate] colorful display of the cvs annotate command
594722a8
ER
2763\\[vc-update-change-log] add change log entry from recent checkins
2764
2765While you are entering a change log message for a version, the following
2766additional bindings will be in effect.
2767
2768\\[vc-finish-logentry] proceed with check in, ending log message entry
2769
2770Whenever you do a checkin, your log comment is added to a ring of
2771saved comments. These can be recalled as follows:
2772
2773\\[vc-next-comment] replace region with next message in comment ring
2774\\[vc-previous-comment] replace region with previous message in comment ring
8c0aaf40
ER
2775\\[vc-comment-search-reverse] search backward for regexp in the comment ring
2776\\[vc-comment-search-forward] search backward for regexp in the comment ring
594722a8 2777
0e362f54
GM
2778Entry to the change-log submode calls the value of `text-mode-hook', then
2779the value of `vc-log-mode-hook'.
594722a8
ER
2780
2781Global user options:
0e362f54 2782 `vc-initial-comment' If non-nil, require user to enter a change
594722a8
ER
2783 comment upon first checkin of the file.
2784
0e362f54 2785 `vc-keep-workfiles' Non-nil value prevents workfiles from being
594722a8
ER
2786 deleted when changes are checked in
2787
0e362f54 2788 `vc-suppress-confirm' Suppresses some confirmation prompts,
594722a8
ER
2789 notably for reversions.
2790
0e362f54 2791 vc-BACKEND-header Which keywords to insert when adding headers
594722a8 2792 with \\[vc-insert-headers]. Defaults to
0e362f54 2793 '(\"\%\W\%\") under SCCS, '(\"\$Id\$\") under
80688f5c 2794 RCS and CVS.
594722a8 2795
0e362f54 2796 `vc-static-header-alist' By default, version headers inserted in C files
594722a8 2797 get stuffed in a static string area so that
80688f5c
RS
2798 ident(RCS/CVS) or what(SCCS) can see them in
2799 the compiled object code. You can override
2800 this by setting this variable to nil, or change
594722a8
ER
2801 the header template by changing it.
2802
0e362f54 2803 `vc-command-messages' if non-nil, display run messages from the
594722a8
ER
2804 actual version-control utilities (this is
2805 intended primarily for people hacking vc
099bd78a
SM
2806 itself)."
2807 (make-local-variable 'vc-comment-ring-index))
0e362f54
GM
2808
2809(defun vc-log-edit (file)
099bd78a
SM
2810 "Set up `log-edit' for use with VC on FILE.
2811If `log-edit' is not available, resort to `vc-log-mode'."
2812 (setq default-directory
2813 (if file (file-name-directory file)
2814 (with-current-buffer vc-parent-buffer default-directory)))
2815 (if (fboundp 'log-edit)
2816 (log-edit 'vc-finish-logentry nil
2817 (if file `(lambda () ',(list (file-name-nondirectory file)))
2818 ;; If FILE is nil, we were called from vc-dired.
2819 (lambda ()
2820 (with-current-buffer vc-parent-buffer
2821 (dired-get-marked-files t)))))
2822 (vc-log-mode))
0e362f54
GM
2823 (set (make-local-variable 'vc-log-file) file)
2824 (make-local-variable 'vc-log-version)
099bd78a 2825 (set-buffer-modified-p nil)
0e362f54 2826 (setq buffer-file-name nil))
594722a8
ER
2827
2828;;; These things should probably be generally available
2829
2f119435
AS
2830(defun vc-file-tree-walk (dirname func &rest args)
2831 "Walk recursively through DIRNAME.
0e362f54 2832Invoke FUNC f ARGS on each VC-managed file f underneath it."
2f119435
AS
2833 (vc-file-tree-walk-internal (expand-file-name dirname) func args)
2834 (message "Traversing directory %s...done" dirname))
02da6253
PE
2835
2836(defun vc-file-tree-walk-internal (file func args)
2837 (if (not (file-directory-p file))
0e362f54 2838 (if (vc-backend file) (apply func file args))
993a1a44 2839 (message "Traversing directory %s..." (abbreviate-file-name file))
02da6253
PE
2840 (let ((dir (file-name-as-directory file)))
2841 (mapcar
0e362f54
GM
2842 (lambda (f) (or
2843 (string-equal f ".")
2844 (string-equal f "..")
2845 (member f vc-directory-exclusion-list)
2846 (let ((dirf (expand-file-name f dir)))
2847 (or
2848 (file-symlink-p dirf);; Avoid possible loops
2849 (vc-file-tree-walk-internal dirf func args)))))
02da6253 2850 (directory-files dir)))))
594722a8
ER
2851
2852(provide 'vc)
2853
2854;;; DEVELOPER'S NOTES ON CONCURRENCY PROBLEMS IN THIS CODE
2855;;;
2856;;; These may be useful to anyone who has to debug or extend the package.
c6d4f628
RS
2857;;; (Note that this information corresponds to versions 5.x. Some of it
2858;;; might have been invalidated by the additions to support branching
2859;;; and RCS keyword lookup. AS, 1995/03/24)
0e362f54 2860;;;
594722a8
ER
2861;;; A fundamental problem in VC is that there are time windows between
2862;;; vc-next-action's computations of the file's version-control state and
2863;;; the actions that change it. This is a window open to lossage in a
2864;;; multi-user environment; someone else could nip in and change the state
2865;;; of the master during it.
0e362f54 2866;;;
594722a8
ER
2867;;; The performance problem is that rlog/prs calls are very expensive; we want
2868;;; to avoid them as much as possible.
0e362f54 2869;;;
594722a8 2870;;; ANALYSIS:
0e362f54 2871;;;
594722a8 2872;;; The performance problem, it turns out, simplifies in practice to the
0e362f54 2873;;; problem of making vc-state fast. The two other functions that call
594722a8
ER
2874;;; prs/rlog will not be so commonly used that the slowdown is a problem; one
2875;;; makes snapshots, the other deletes the calling user's last change in the
2876;;; master.
0e362f54 2877;;;
594722a8
ER
2878;;; The race condition implies that we have to either (a) lock the master
2879;;; during the entire execution of vc-next-action, or (b) detect and
2880;;; recover from errors resulting from dispatch on an out-of-date state.
0e362f54 2881;;;
a7acbbe4 2882;;; Alternative (a) appears to be infeasible. The problem is that we can't
594722a8
ER
2883;;; guarantee that the lock will ever be removed. Suppose a user starts a
2884;;; checkin, the change message buffer pops up, and the user, having wandered
2885;;; off to do something else, simply forgets about it?
0e362f54 2886;;;
594722a8 2887;;; Alternative (b), on the other hand, works well with a cheap way to speed up
0e362f54 2888;;; vc-state. Usually, if a file is registered, we can read its locked/
594722a8 2889;;; unlocked state and its current owner from its permissions.
0e362f54 2890;;;
594722a8
ER
2891;;; This shortcut will fail if someone has manually changed the workfile's
2892;;; permissions; also if developers are munging the workfile in several
2893;;; directories, with symlinks to a master (in this latter case, the
2894;;; permissions shortcut will fail to detect a lock asserted from another
2895;;; directory).
0e362f54 2896;;;
594722a8
ER
2897;;; Note that these cases correspond exactly to the errors which could happen
2898;;; because of a competing checkin/checkout race in between two instances of
2899;;; vc-next-action.
0e362f54 2900;;;
594722a8 2901;;; For VC's purposes, a workfile/master pair may have the following states:
0e362f54 2902;;;
594722a8 2903;;; A. Unregistered. There is a workfile, there is no master.
0e362f54 2904;;;
594722a8 2905;;; B. Registered and not locked by anyone.
0e362f54 2906;;;
594722a8 2907;;; C. Locked by calling user and unchanged.
0e362f54 2908;;;
594722a8 2909;;; D. Locked by the calling user and changed.
0e362f54 2910;;;
594722a8 2911;;; E. Locked by someone other than the calling user.
0e362f54 2912;;;
594722a8 2913;;; This makes for 25 states and 20 error conditions. Here's the matrix:
0e362f54 2914;;;
594722a8
ER
2915;;; VC's idea of state
2916;;; |
2917;;; V Actual state RCS action SCCS action Effect
2918;;; A B C D E
2919;;; A . 1 2 3 4 ci -u -t- admin -fb -i<file> initial admin
2920;;; B 5 . 6 7 8 co -l get -e checkout
2921;;; C 9 10 . 11 12 co -u unget; get revert
2922;;; D 13 14 15 . 16 ci -u -m<comment> delta -y<comment>; get checkin
b0c9bc8c 2923;;; E 17 18 19 20 . rcs -u -M -l unget -n ; get -g steal lock
0e362f54 2924;;;
594722a8 2925;;; All commands take the master file name as a last argument (not shown).
0e362f54 2926;;;
594722a8
ER
2927;;; In the discussion below, a "self-race" is a pathological situation in
2928;;; which VC operations are being attempted simultaneously by two or more
2929;;; Emacsen running under the same username.
0e362f54 2930;;;
594722a8 2931;;; The vc-next-action code has the following windows:
0e362f54 2932;;;
594722a8
ER
2933;;; Window P:
2934;;; Between the check for existence of a master file and the call to
2935;;; admin/checkin in vc-buffer-admin (apparent state A). This window may
2936;;; never close if the initial-comment feature is on.
0e362f54 2937;;;
594722a8
ER
2938;;; Window Q:
2939;;; Between the call to vc-workfile-unchanged-p in and the immediately
2940;;; following revert (apparent state C).
0e362f54 2941;;;
594722a8
ER
2942;;; Window R:
2943;;; Between the call to vc-workfile-unchanged-p in and the following
2944;;; checkin (apparent state D). This window may never close.
0e362f54 2945;;;
594722a8
ER
2946;;; Window S:
2947;;; Between the unlock and the immediately following checkout during a
2948;;; revert operation (apparent state C). Included in window Q.
0e362f54 2949;;;
594722a8 2950;;; Window T:
0e362f54
GM
2951;;; Between vc-state and the following checkout (apparent state B).
2952;;;
594722a8 2953;;; Window U:
0e362f54 2954;;; Between vc-state and the following revert (apparent state C).
594722a8 2955;;; Includes windows Q and S.
0e362f54 2956;;;
594722a8 2957;;; Window V:
0e362f54 2958;;; Between vc-state and the following checkin (apparent state
594722a8
ER
2959;;; D). This window may never be closed if the user fails to complete the
2960;;; checkin message. Includes window R.
0e362f54 2961;;;
594722a8 2962;;; Window W:
0e362f54 2963;;; Between vc-state and the following steal-lock (apparent
34291cd2 2964;;; state E). This window may never close if the user fails to complete
594722a8 2965;;; the steal-lock message. Includes window X.
0e362f54 2966;;;
594722a8
ER
2967;;; Window X:
2968;;; Between the unlock and the immediately following re-lock during a
0e362f54 2969;;; steal-lock operation (apparent state E). This window may never close
594722a8 2970;;; if the user fails to complete the steal-lock message.
0e362f54 2971;;;
594722a8 2972;;; Errors:
0e362f54 2973;;;
594722a8
ER
2974;;; Apparent state A ---
2975;;;
2976;;; 1. File looked unregistered but is actually registered and not locked.
0e362f54 2977;;;
594722a8
ER
2978;;; Potential cause: someone else's admin during window P, with
2979;;; caller's admin happening before their checkout.
0e362f54 2980;;;
b0c9bc8c
AS
2981;;; RCS: Prior to version 5.6.4, ci fails with message
2982;;; "no lock set by <user>". From 5.6.4 onwards, VC uses the new
2983;;; ci -i option and the message is "<file>,v: already exists".
594722a8 2984;;; SCCS: admin will fail with error (ad19).
0e362f54 2985;;;
594722a8 2986;;; We can let these errors be passed up to the user.
0e362f54 2987;;;
594722a8 2988;;; 2. File looked unregistered but is actually locked by caller, unchanged.
0e362f54 2989;;;
594722a8 2990;;; Potential cause: self-race during window P.
0e362f54 2991;;;
b0c9bc8c
AS
2992;;; RCS: Prior to version 5.6.4, reverts the file to the last saved
2993;;; version and unlocks it. From 5.6.4 onwards, VC uses the new
2994;;; ci -i option, failing with message "<file>,v: already exists".
594722a8 2995;;; SCCS: will fail with error (ad19).
0e362f54 2996;;;
594722a8 2997;;; Either of these consequences is acceptable.
0e362f54 2998;;;
594722a8 2999;;; 3. File looked unregistered but is actually locked by caller, changed.
0e362f54 3000;;;
594722a8 3001;;; Potential cause: self-race during window P.
0e362f54
GM
3002;;;
3003;;; RCS: Prior to version 5.6.4, VC registers the caller's workfile as
3004;;; a delta with a null change comment (the -t- switch will be
b0c9bc8c
AS
3005;;; ignored). From 5.6.4 onwards, VC uses the new ci -i option,
3006;;; failing with message "<file>,v: already exists".
594722a8 3007;;; SCCS: will fail with error (ad19).
0e362f54 3008;;;
594722a8 3009;;; 4. File looked unregistered but is locked by someone else.
0e362f54 3010;;;
594722a8
ER
3011;;; Potential cause: someone else's admin during window P, with
3012;;; caller's admin happening *after* their checkout.
0e362f54
GM
3013;;;
3014;;; RCS: Prior to version 5.6.4, ci fails with a
3015;;; "no lock set by <user>" message. From 5.6.4 onwards,
3016;;; VC uses the new ci -i option, failing with message
b0c9bc8c 3017;;; "<file>,v: already exists".
594722a8 3018;;; SCCS: will fail with error (ad19).
0e362f54 3019;;;
594722a8 3020;;; We can let these errors be passed up to the user.
0e362f54 3021;;;
594722a8
ER
3022;;; Apparent state B ---
3023;;;
3024;;; 5. File looked registered and not locked, but is actually unregistered.
0e362f54 3025;;;
594722a8 3026;;; Potential cause: master file got nuked during window P.
0e362f54 3027;;;
594722a8
ER
3028;;; RCS: will fail with "RCS/<file>: No such file or directory"
3029;;; SCCS: will fail with error ut4.
0e362f54 3030;;;
594722a8 3031;;; We can let these errors be passed up to the user.
0e362f54 3032;;;
594722a8
ER
3033;;; 6. File looked registered and not locked, but is actually locked by the
3034;;; calling user and unchanged.
0e362f54 3035;;;
594722a8 3036;;; Potential cause: self-race during window T.
0e362f54 3037;;;
594722a8
ER
3038;;; RCS: in the same directory as the previous workfile, co -l will fail
3039;;; with "co error: writable foo exists; checkout aborted". In any other
3040;;; directory, checkout will succeed.
3041;;; SCCS: will fail with ge17.
0e362f54 3042;;;
594722a8 3043;;; Either of these consequences is acceptable.
0e362f54 3044;;;
594722a8
ER
3045;;; 7. File looked registered and not locked, but is actually locked by the
3046;;; calling user and changed.
0e362f54 3047;;;
594722a8 3048;;; As case 6.
0e362f54 3049;;;
594722a8
ER
3050;;; 8. File looked registered and not locked, but is actually locked by another
3051;;; user.
0e362f54 3052;;;
594722a8 3053;;; Potential cause: someone else checks it out during window T.
0e362f54 3054;;;
594722a8
ER
3055;;; RCS: co error: revision 1.3 already locked by <user>
3056;;; SCCS: fails with ge4 (in directory) or ut7 (outside it).
0e362f54 3057;;;
594722a8 3058;;; We can let these errors be passed up to the user.
0e362f54 3059;;;
594722a8
ER
3060;;; Apparent state C ---
3061;;;
3062;;; 9. File looks locked by calling user and unchanged, but is unregistered.
0e362f54 3063;;;
594722a8 3064;;; As case 5.
0e362f54 3065;;;
594722a8
ER
3066;;; 10. File looks locked by calling user and unchanged, but is actually not
3067;;; locked.
0e362f54 3068;;;
594722a8
ER
3069;;; Potential cause: a self-race in window U, or by the revert's
3070;;; landing during window X of some other user's steal-lock or window S
3071;;; of another user's revert.
0e362f54 3072;;;
594722a8
ER
3073;;; RCS: succeeds, refreshing the file from the identical version in
3074;;; the master.
3075;;; SCCS: fails with error ut4 (p file nonexistent).
3076;;;
3077;;; Either of these consequences is acceptable.
0e362f54 3078;;;
594722a8
ER
3079;;; 11. File is locked by calling user. It looks unchanged, but is actually
3080;;; changed.
0e362f54 3081;;;
594722a8
ER
3082;;; Potential cause: the file would have to be touched by a self-race
3083;;; during window Q.
0e362f54 3084;;;
594722a8
ER
3085;;; The revert will succeed, removing whatever changes came with
3086;;; the touch. It is theoretically possible that work could be lost.
0e362f54 3087;;;
594722a8
ER
3088;;; 12. File looks like it's locked by the calling user and unchanged, but
3089;;; it's actually locked by someone else.
0e362f54 3090;;;
594722a8 3091;;; Potential cause: a steal-lock in window V.
0e362f54 3092;;;
594722a8
ER
3093;;; RCS: co error: revision <rev> locked by <user>; use co -r or rcs -u
3094;;; SCCS: fails with error un2
0e362f54 3095;;;
594722a8 3096;;; We can pass these errors up to the user.
0e362f54 3097;;;
594722a8
ER
3098;;; Apparent state D ---
3099;;;
3100;;; 13. File looks like it's locked by the calling user and changed, but it's
3101;;; actually unregistered.
0e362f54 3102;;;
594722a8 3103;;; Potential cause: master file got nuked during window P.
0e362f54
GM
3104;;;
3105;;; RCS: Prior to version 5.6.4, checks in the user's version as an
b0c9bc8c
AS
3106;;; initial delta. From 5.6.4 onwards, VC uses the new ci -j
3107;;; option, failing with message "no such file or directory".
594722a8
ER
3108;;; SCCS: will fail with error ut4.
3109;;;
b0c9bc8c
AS
3110;;; This case is kind of nasty. Under RCS prior to version 5.6.4,
3111;;; VC may fail to detect the loss of previous version information.
0e362f54 3112;;;
594722a8
ER
3113;;; 14. File looks like it's locked by the calling user and changed, but it's
3114;;; actually unlocked.
0e362f54 3115;;;
594722a8
ER
3116;;; Potential cause: self-race in window V, or the checkin happening
3117;;; during the window X of someone else's steal-lock or window S of
3118;;; someone else's revert.
0e362f54 3119;;;
594722a8
ER
3120;;; RCS: ci will fail with "no lock set by <user>".
3121;;; SCCS: delta will fail with error ut4.
0e362f54 3122;;;
594722a8
ER
3123;;; 15. File looks like it's locked by the calling user and changed, but it's
3124;;; actually locked by the calling user and unchanged.
0e362f54 3125;;;
594722a8
ER
3126;;; Potential cause: another self-race --- a whole checkin/checkout
3127;;; sequence by the calling user would have to land in window R.
0e362f54 3128;;;
594722a8
ER
3129;;; SCCS: checks in a redundant delta and leaves the file unlocked as usual.
3130;;; RCS: reverts to the file state as of the second user's checkin, leaving
3131;;; the file unlocked.
3132;;;
3133;;; It is theoretically possible that work could be lost under RCS.
0e362f54 3134;;;
594722a8
ER
3135;;; 16. File looks like it's locked by the calling user and changed, but it's
3136;;; actually locked by a different user.
0e362f54 3137;;;
594722a8
ER
3138;;; RCS: ci error: no lock set by <user>
3139;;; SCCS: unget will fail with error un2
0e362f54 3140;;;
594722a8 3141;;; We can pass these errors up to the user.
0e362f54 3142;;;
594722a8
ER
3143;;; Apparent state E ---
3144;;;
3145;;; 17. File looks like it's locked by some other user, but it's actually
3146;;; unregistered.
0e362f54 3147;;;
594722a8 3148;;; As case 13.
0e362f54 3149;;;
594722a8
ER
3150;;; 18. File looks like it's locked by some other user, but it's actually
3151;;; unlocked.
0e362f54 3152;;;
594722a8 3153;;; Potential cause: someone released a lock during window W.
0e362f54 3154;;;
594722a8
ER
3155;;; RCS: The calling user will get the lock on the file.
3156;;; SCCS: unget -n will fail with cm4.
0e362f54 3157;;;
594722a8 3158;;; Either of these consequences will be OK.
0e362f54 3159;;;
594722a8
ER
3160;;; 19. File looks like it's locked by some other user, but it's actually
3161;;; locked by the calling user and unchanged.
0e362f54 3162;;;
594722a8
ER
3163;;; Potential cause: the other user relinquishing a lock followed by
3164;;; a self-race, both in window W.
0e362f54 3165;;;
594722a8
ER
3166;;; Under both RCS and SCCS, both unlock and lock will succeed, making
3167;;; the sequence a no-op.
0e362f54 3168;;;
594722a8
ER
3169;;; 20. File looks like it's locked by some other user, but it's actually
3170;;; locked by the calling user and changed.
0e362f54 3171;;;
594722a8 3172;;; As case 19.
0e362f54 3173;;;
594722a8 3174;;; PROBLEM CASES:
0e362f54 3175;;;
594722a8 3176;;; In order of decreasing severity:
0e362f54 3177;;;
b0c9bc8c 3178;;; Cases 11 and 15 are the only ones that potentially lose work.
594722a8 3179;;; They would require a self-race for this to happen.
0e362f54 3180;;;
594722a8
ER
3181;;; Case 13 in RCS loses information about previous deltas, retaining
3182;;; only the information in the current workfile. This can only happen
3183;;; if the master file gets nuked in window P.
0e362f54 3184;;;
594722a8
ER
3185;;; Case 3 in RCS and case 15 under SCCS insert a redundant delta with
3186;;; no change comment in the master. This would require a self-race in
3187;;; window P or R respectively.
0e362f54 3188;;;
594722a8 3189;;; Cases 2, 10, 19 and 20 do extra work, but make no changes.
0e362f54 3190;;;
594722a8
ER
3191;;; Unfortunately, it appears to me that no recovery is possible in these
3192;;; cases. They don't yield error messages, so there's no way to tell that
3193;;; a race condition has occurred.
0e362f54 3194;;;
594722a8
ER
3195;;; All other cases don't change either the workfile or the master, and
3196;;; trigger command errors which the user will see.
0e362f54 3197;;;
594722a8
ER
3198;;; Thus, there is no explicit recovery code.
3199
3200;;; vc.el ends here