(diary-face, holiday-face): Add dark-background variants.
[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
f9b59b2b 8;; $Id: vc.el,v 1.284 2000/10/26 20:53:11 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>
166a0ef7 36;; Dave Love <fx@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)
347bef30
SM
1200 (when comment
1201 (erase-buffer)
1202 (when (stringp comment) (insert comment)))
1203 (if (or (not comment) initial-contents)
1204 (message "%s Type C-c C-c when done" msg)
1205 (vc-finish-logentry (eq comment t)))))
e1f297e6 1206
c6d4f628 1207(defun vc-checkout (file &optional writable rev)
099bd78a
SM
1208 "Retrieve a copy of the revision REV of FILE.
1209If WRITABLE is non-nil, make sure the retrieved file is writable.
1210REV defaults to the latest revision."
ffda0460
AS
1211 (and writable
1212 (not rev)
10b48cc4 1213 (vc-call make-version-backups-p file)
ffda0460 1214 (vc-up-to-date-p file)
10b48cc4 1215 (vc-make-version-backup file))
099bd78a
SM
1216 (with-vc-properties
1217 file
1218 (condition-case err
1219 (vc-call checkout file writable rev)
1220 (file-error
1221 ;; Maybe the backend is not installed ;-(
1222 (when writable
1223 (let ((buf (get-file-buffer file)))
1224 (when buf (with-current-buffer buf (toggle-read-only -1)))))
1225 (signal (car err) (cdr err))))
a3255400
SM
1226 `((vc-state . ,(if (or (eq (vc-checkout-model file) 'implicit)
1227 (not writable))
1228 (if (vc-call latest-on-branch-p file)
1229 'up-to-date
1230 'needs-patch)
1231 'edited))
1232 (vc-checkout-time . ,(nth 5 (file-attributes file)))))
b0c9bc8c 1233 (vc-resynch-buffer file t t))
594722a8 1234
0e362f54 1235(defun vc-steal-lock (file rev owner)
099bd78a 1236 "Steal the lock on FILE."
29fc1ce9 1237 (let (file-description)
29fc1ce9
RS
1238 (if rev
1239 (setq file-description (format "%s:%s" file rev))
1240 (setq file-description file))
4bc504c8
RS
1241 (if (not (yes-or-no-p (format "Steal the lock on %s from %s? "
1242 file-description owner)))
0e362f54
GM
1243 (error "Steal canceled"))
1244 (compose-mail owner (format "Stolen lock on %s" file-description)
1245 nil nil nil nil
1246 (list (list 'vc-finish-steal file rev)))
29fc1ce9 1247 (setq default-directory (expand-file-name "~/"))
29fc1ce9
RS
1248 (goto-char (point-max))
1249 (insert
1250 (format "I stole the lock on %s, " file-description)
1251 (current-time-string)
1252 ".\n")
1253 (message "Please explain why you stole the lock. Type C-c C-c when done.")))
594722a8
ER
1254
1255(defun vc-finish-steal (file version)
0e362f54
GM
1256 ;; This is called when the notification has been sent.
1257 (message "Stealing lock on %s..." file)
46e33aee 1258 (with-vc-properties
099bd78a
SM
1259 file
1260 (vc-call steal-lock file version)
a3255400 1261 `((vc-state . edited)))
0e362f54
GM
1262 (vc-resynch-buffer file t t)
1263 (message "Stealing lock on %s...done" file))
594722a8 1264
0ab66291 1265(defun vc-checkin (file &optional rev comment initial-contents)
6f41eeb5 1266 "Check in FILE.
0e362f54
GM
1267The optional argument REV may be a string specifying the new version
1268level (if nil increment the current level). COMMENT is a comment
0ab66291
AS
1269string; if omitted, a buffer is popped up to accept a comment. If
1270INITIAL-CONTENTS is non-nil, then COMMENT is used as the initial contents
1271of the log entry buffer.
0e362f54
GM
1272
1273If `vc-keep-workfiles' is nil, FILE is deleted afterwards, provided
1274that the version control system supports this mode of operation.
861f3c29
DL
1275
1276Runs the normal hook `vc-checkin-hook'."
6f41eeb5 1277 (vc-start-entry
0ab66291 1278 file rev comment initial-contents
6f41eeb5 1279 "Enter a change comment."
0e362f54
GM
1280 (lambda (file rev comment)
1281 (message "Checking in %s..." file)
1282 ;; "This log message intentionally left almost blank".
1283 ;; RCS 5.7 gripes about white-space-only comments too.
1284 (or (and comment (string-match "[^\t\n ]" comment))
1285 (setq comment "*** empty log message ***"))
46e33aee 1286 (with-vc-properties
099bd78a
SM
1287 file
1288 ;; Change buffers to get local value of vc-checkin-switches.
1289 (with-current-buffer (or (get-file-buffer file) (current-buffer))
ffda0460
AS
1290 (let ((backup-file (vc-version-backup-file file)))
1291 (vc-call checkin file rev comment)
1292 (if backup-file (delete-file backup-file))))
a3255400
SM
1293 `((vc-state . up-to-date)
1294 (vc-checkout-time . ,(nth 5 (file-attributes file)))
1295 (vc-workfile-version . nil)))
0e362f54
GM
1296 (message "Checking in %s...done" file))
1297 'vc-checkin-hook))
594722a8 1298
3b4dd9a9
RM
1299(defun vc-comment-to-change-log (&optional whoami file-name)
1300 "Enter last VC comment into change log file for current buffer's file.
1301Optional arg (interactive prefix) non-nil means prompt for user name and site.
1302Second arg is file name of change log. \
861f3c29
DL
1303If nil, uses `change-log-default-name'.
1304
1305May be useful as a `vc-checkin-hook' to update change logs automatically."
43cea1ab
RM
1306 (interactive (if current-prefix-arg
1307 (list current-prefix-arg
1308 (prompt-for-change-log-name))))
41208291
KH
1309 ;; Make sure the defvar for add-log-current-defun-function has been executed
1310 ;; before binding it.
1311 (require 'add-log)
3b4dd9a9
RM
1312 (let (;; Extract the comment first so we get any error before doing anything.
1313 (comment (ring-ref vc-comment-ring 0))
43cea1ab 1314 ;; Don't let add-change-log-entry insert a defun name.
3b4dd9a9
RM
1315 (add-log-current-defun-function 'ignore)
1316 end)
1317 ;; Call add-log to do half the work.
43cea1ab 1318 (add-change-log-entry whoami file-name t t)
3b4dd9a9
RM
1319 ;; Insert the VC comment, leaving point before it.
1320 (setq end (save-excursion (insert comment) (point-marker)))
1321 (if (looking-at "\\s *\\s(")
1322 ;; It starts with an open-paren, as in "(foo): Frobbed."
43cea1ab 1323 ;; So remove the ": " add-log inserted.
3b4dd9a9
RM
1324 (delete-char -2))
1325 ;; Canonicalize the white space between the file name and comment.
1326 (just-one-space)
1327 ;; Indent rest of the text the same way add-log indented the first line.
1328 (let ((indentation (current-indentation)))
1329 (save-excursion
1330 (while (< (point) end)
1331 (forward-line 1)
1332 (indent-to indentation))
c124b1b4 1333 (setq end (point))))
3b4dd9a9 1334 ;; Fill the inserted text, preserving open-parens at bol.
6b60c5d1
BG
1335 (let ((paragraph-separate (concat paragraph-separate "\\|\\s *\\s("))
1336 (paragraph-start (concat paragraph-start "\\|\\s *\\s(")))
43cea1ab 1337 (beginning-of-line)
c124b1b4
RM
1338 (fill-region (point) end))
1339 ;; Canonicalize the white space at the end of the entry so it is
1340 ;; separated from the next entry by a single blank line.
1341 (skip-syntax-forward " " end)
1342 (delete-char (- (skip-syntax-backward " ")))
1343 (or (eobp) (looking-at "\n\n")
1344 (insert "\n"))))
3b4dd9a9 1345
8c0aaf40 1346(defun vc-finish-logentry (&optional nocomment)
594722a8
ER
1347 "Complete the operation implied by the current log entry."
1348 (interactive)
8c0aaf40 1349 ;; Check and record the comment, if any.
0e362f54
GM
1350 (unless nocomment
1351 ;; Comment too long?
1352 (vc-call-backend (or (and vc-log-file (vc-backend vc-log-file))
1353 (vc-responsible-backend default-directory))
1354 'logentry-check)
1355 (run-hooks 'vc-logentry-check-hook)
1356 ;; Record the comment in the comment ring
1357 (let ((comment (buffer-string)))
1358 (unless (and (ring-p vc-comment-ring)
1359 (not (ring-empty-p vc-comment-ring))
1360 (equal comment (ring-ref vc-comment-ring 0)))
1361 (ring-insert vc-comment-ring comment))))
b2396d1f 1362 ;; Sync parent buffer in case the user modified it while editing the comment.
cdaf7a1a 1363 ;; But not if it is a vc-dired buffer.
0e362f54
GM
1364 (with-current-buffer vc-parent-buffer
1365 (or vc-dired-mode (vc-buffer-sync)))
61dee1e7
AS
1366 (if (not vc-log-operation) (error "No log operation is pending"))
1367 ;; save the parameters held in buffer-local variables
1368 (let ((log-operation vc-log-operation)
1369 (log-file vc-log-file)
1370 (log-version vc-log-version)
1371 (log-entry (buffer-string))
2c4eea90
KH
1372 (after-hook vc-log-after-operation-hook)
1373 (tmp-vc-parent-buffer vc-parent-buffer))
e2bef5c3 1374 (pop-to-buffer vc-parent-buffer)
61dee1e7
AS
1375 ;; OK, do it to it
1376 (save-excursion
0e362f54 1377 (funcall log-operation
61dee1e7
AS
1378 log-file
1379 log-version
1380 log-entry))
df1e7b91
KH
1381 ;; Remove checkin window (after the checkin so that if that fails
1382 ;; we don't zap the *VC-log* buffer and the typing therein).
1383 (let ((logbuf (get-buffer "*VC-log*")))
2c4eea90
KH
1384 (cond ((and logbuf vc-delete-logbuf-window)
1385 (delete-windows-on logbuf (selected-frame))
262c8cea 1386 ;; Kill buffer and delete any other dedicated windows/frames.
2c4eea90 1387 (kill-buffer logbuf))
0ab66291
AS
1388 (logbuf (pop-to-buffer "*VC-log*")
1389 (bury-buffer)
1390 (pop-to-buffer tmp-vc-parent-buffer))))
e2bef5c3 1391 ;; Now make sure we see the expanded headers
46e33aee 1392 (if log-file
0ab66291 1393 (vc-resynch-buffer log-file vc-keep-workfiles t))
0e362f54 1394 (if vc-dired-mode
0ab66291 1395 (dired-move-to-filename))
37667a5c 1396 (run-hooks after-hook 'vc-finish-logentry-hook)))
594722a8
ER
1397
1398;; Code for access to the comment ring
1399
0e362f54
GM
1400(defun vc-new-comment-index (stride len)
1401 (mod (cond
1402 (vc-comment-ring-index (+ vc-comment-ring-index stride))
1403 ;; Initialize the index on the first use of this command
1404 ;; so that the first M-p gets index 0, and the first M-n gets
1405 ;; index -1.
1406 ((> stride 0) (1- stride))
1407 (t stride))
1408 len))
1409
8c0aaf40
ER
1410(defun vc-previous-comment (arg)
1411 "Cycle backwards through comment history."
1412 (interactive "*p")
1413 (let ((len (ring-length vc-comment-ring)))
0e362f54
GM
1414 (if (<= len 0)
1415 (progn (message "Empty comment ring") (ding))
1416 (erase-buffer)
1417 (setq vc-comment-ring-index (vc-new-comment-index arg len))
1418 (message "Comment %d" (1+ vc-comment-ring-index))
1419 (insert (ring-ref vc-comment-ring vc-comment-ring-index)))))
8c0aaf40
ER
1420
1421(defun vc-next-comment (arg)
1422 "Cycle forwards through comment history."
1423 (interactive "*p")
1424 (vc-previous-comment (- arg)))
1425
0e362f54 1426(defun vc-comment-search-reverse (str &optional stride)
a3255400 1427 "Search backwards through comment history for substring match."
0e362f54
GM
1428 ;; Why substring rather than regexp ? -sm
1429 (interactive
1430 (list (read-string "Comment substring: " nil nil vc-last-comment-match)))
1431 (unless stride (setq stride 1))
8c0aaf40
ER
1432 (if (string= str "")
1433 (setq str vc-last-comment-match)
1434 (setq vc-last-comment-match str))
0e362f54
GM
1435 (let* ((str (regexp-quote str))
1436 (len (ring-length vc-comment-ring))
1437 (n (vc-new-comment-index stride len)))
1438 (while (progn (when (or (>= n len) (< n 0)) (error "Not found"))
1439 (not (string-match str (ring-ref vc-comment-ring n))))
1440 (setq n (+ n stride)))
1441 (setq vc-comment-ring-index n)
1442 (vc-previous-comment 0)))
8c0aaf40
ER
1443
1444(defun vc-comment-search-forward (str)
a3255400 1445 "Search forwards through comment history for substring match."
0e362f54
GM
1446 (interactive
1447 (list (read-string "Comment substring: " nil nil vc-last-comment-match)))
1448 (vc-comment-search-reverse str -1))
594722a8
ER
1449
1450;; Additional entry points for examining version histories
1451
637a8ae9 1452;;;###autoload
97d3f950 1453(defun vc-diff (historic &optional not-urgent)
e8ee1ccf 1454 "Display diffs between file versions.
0e362f54 1455Normally this compares the current file and buffer with the most recent
e8ee1ccf
RS
1456checked in version of that file. This uses no arguments.
1457With a prefix argument, it reads the file name to use
1458and two version designators specifying which versions to compare."
48078f8f 1459 (interactive (list current-prefix-arg t))
b6909007 1460 (vc-ensure-vc-buffer)
594722a8
ER
1461 (if historic
1462 (call-interactively 'vc-version-diff)
0e362f54 1463 (let ((file buffer-file-name))
c0d66cb2 1464 (vc-buffer-sync not-urgent)
0e362f54
GM
1465 (if (vc-workfile-unchanged-p buffer-file-name)
1466 (message "No changes to %s since latest version" file)
1467 (vc-version-diff file nil nil)))))
594722a8
ER
1468
1469(defun vc-version-diff (file rel1 rel2)
1470 "For FILE, report diffs between two stored versions REL1 and REL2 of it.
1471If FILE is a directory, generate diffs between versions for all registered
1472files in or below it."
0e362f54 1473 (interactive
4e6473c8
GM
1474 (let ((file (expand-file-name
1475 (read-file-name (if buffer-file-name
1476 "File or dir to diff: (default visited file) "
1477 "File or dir to diff: ")
1478 default-directory buffer-file-name t)))
c0d66cb2
RS
1479 (rel1-default nil) (rel2-default nil))
1480 ;; compute default versions based on the file state
1481 (cond
0e362f54
GM
1482 ;; if it's a directory, don't supply any version default
1483 ((file-directory-p file)
c0d66cb2 1484 nil)
0e362f54
GM
1485 ;; if the file is not up-to-date, use current version as older version
1486 ((not (vc-up-to-date-p file))
c0d66cb2
RS
1487 (setq rel1-default (vc-workfile-version file)))
1488 ;; if the file is not locked, use last and previous version as default
1489 (t
1490 (setq rel1-default (vc-previous-version (vc-workfile-version file)))
0e362f54 1491 (if (string= rel1-default "") (setq rel1-default nil))
c0d66cb2
RS
1492 (setq rel2-default (vc-workfile-version file))))
1493 ;; construct argument list
0e362f54 1494 (list file
8e710301
RS
1495 (read-string (if rel1-default
1496 (concat "Older version: (default "
1497 rel1-default ") ")
1498 "Older version: ")
1499 nil nil rel1-default)
1500 (read-string (if rel2-default
1501 (concat "Newer version: (default "
1502 rel2-default ") ")
ba27415c 1503 "Newer version (default: current source): ")
8e710301 1504 nil nil rel2-default))))
0e362f54 1505 (vc-setup-buffer "*vc-diff*")
594722a8 1506 (if (file-directory-p file)
ffda0460 1507 ;; recursive directory diff
0e362f54 1508 (let ((inhibit-read-only t))
ffda0460
AS
1509 (if (string-equal rel1 "") (setq rel1 nil))
1510 (if (string-equal rel2 "") (setq rel2 nil))
3234e2a3
ER
1511 (insert "Diffs between "
1512 (or rel1 "last version checked in")
1513 " and "
1514 (or rel2 "current workfile(s)")
1515 ":\n\n")
0e362f54
GM
1516 (setq default-directory (file-name-as-directory file))
1517 ;; FIXME: this should do a single exec in CVS.
594722a8 1518 (vc-file-tree-walk
2f119435 1519 default-directory
0e362f54
GM
1520 (lambda (f)
1521 (vc-exec-after
1522 `(progn
1523 (message "Looking at %s" ',f)
1524 (vc-call-backend ',(vc-backend file) 'diff ',f ',rel1 ',rel2)))))
1525 (vc-exec-after `(let ((inhibit-read-only t))
1526 (insert "\nEnd of diffs.\n"))))
ffda0460
AS
1527 ;; single file diff
1528 (if (or (not rel1) (string-equal rel1 ""))
1529 (setq rel1 (vc-workfile-version file)))
1530 (if (string-equal rel2 "")
1531 (setq rel2 nil))
1532 (let ((file-rel1 (vc-version-backup-file file rel1))
8f4f0214
SM
1533 (file-rel2 (if (not rel2)
1534 file
ffda0460
AS
1535 (vc-version-backup-file file rel2))))
1536 (if (and file-rel1 file-rel2)
8f4f0214
SM
1537 (apply 'vc-do-command t 1 "diff" nil
1538 (append (if (listp diff-switches)
1539 diff-switches
1540 (list diff-switches))
1541 (list (file-relative-name file-rel1)
1542 (file-relative-name file-rel2))))
ffda0460
AS
1543 (cd (file-name-directory file))
1544 (vc-call diff file rel1 rel2))))
0e362f54
GM
1545 (if (and (zerop (buffer-size))
1546 (not (get-buffer-process (current-buffer))))
1547 (progn
1548 (if rel1
1549 (if rel2
1550 (message "No changes to %s between %s and %s" file rel1 rel2)
1551 (message "No changes to %s since %s" file rel1))
1552 (message "No changes to %s since latest version" file))
1553 nil)
1554 (pop-to-buffer (current-buffer))
1555 ;; Gnus-5.8.5 sets up an autoload for diff-mode, even if it's
1556 ;; not available. Work around that.
1557 (if (require 'diff-mode nil t) (diff-mode))
1d502d5a
AS
1558 (vc-exec-after '(progn (if (eq (buffer-size) 0)
1559 (insert "No differences found.\n"))
1560 (goto-char (point-min))
0e362f54
GM
1561 (shrink-window-if-larger-than-buffer)))
1562 t))
594722a8 1563
f1818994
PE
1564;;;###autoload
1565(defun vc-version-other-window (rev)
1566 "Visit version REV of the current buffer in another window.
1567If the current buffer is named `F', the version is named `F.~REV~'.
1568If `F.~REV~' already exists, it is used instead of being re-created."
0e362f54 1569 (interactive "sVersion to visit (default is workfile version): ")
b6909007 1570 (vc-ensure-vc-buffer)
5e011cb2
SM
1571 (let* ((file buffer-file-name)
1572 (version (if (string-equal rev "")
1573 (vc-workfile-version file)
b6909007 1574 rev))
10b48cc4
AS
1575 (automatic-backup (vc-version-backup-file-name file version))
1576 (manual-backup (vc-version-backup-file-name file version 'manual)))
1577 (unless (file-exists-p manual-backup)
1578 (if (file-exists-p automatic-backup)
f9b59b2b 1579 (rename-file automatic-backup manual-backup nil)
5e011cb2 1580 (vc-call checkout file nil version manual-backup)))
10b48cc4 1581 (find-file-other-window manual-backup)))
f1818994 1582
594722a8
ER
1583;; Header-insertion code
1584
637a8ae9 1585;;;###autoload
594722a8 1586(defun vc-insert-headers ()
099bd78a 1587 "Insert headers in a file for use with your version control system.
b524ce9f 1588Headers desired are inserted at point, and are pulled from
0e362f54 1589the variable `vc-BACKEND-header'."
594722a8 1590 (interactive)
b6909007 1591 (vc-ensure-vc-buffer)
594722a8
ER
1592 (save-excursion
1593 (save-restriction
1594 (widen)
1595 (if (or (not (vc-check-headers))
820bde8d 1596 (y-or-n-p "Version headers already exist. Insert another set? "))
594722a8
ER
1597 (progn
1598 (let* ((delims (cdr (assq major-mode vc-comment-alist)))
1599 (comment-start-vc (or (car delims) comment-start "#"))
1600 (comment-end-vc (or (car (cdr delims)) comment-end ""))
0e362f54
GM
1601 (hdsym (vc-make-backend-sym (vc-backend (buffer-file-name))
1602 'header))
1603 (hdstrings (and (boundp hdsym) (symbol-value hdsym))))
1604 (mapcar (lambda (s)
1605 (insert comment-start-vc "\t" s "\t"
1606 comment-end-vc "\n"))
594722a8
ER
1607 hdstrings)
1608 (if vc-static-header-alist
0e362f54
GM
1609 (mapcar (lambda (f)
1610 (if (string-match (car f) buffer-file-name)
1611 (insert (format (cdr f) (car hdstrings)))))
594722a8
ER
1612 vc-static-header-alist))
1613 )
1614 )))))
1615
0e362f54 1616(defun vc-clear-headers (&optional file)
099bd78a
SM
1617 "Clear all version headers in the current buffer (or FILE).
1618I.e. reset them to the non-expanded form."
0e362f54
GM
1619 (let* ((filename (or file buffer-file-name))
1620 (visited (find-buffer-visiting filename))
1621 (backend (vc-backend filename)))
1622 (when (vc-find-backend-function backend 'clear-headers)
6f41eeb5 1623 (if visited
0e362f54
GM
1624 (let ((context (vc-buffer-context)))
1625 ;; save-excursion may be able to relocate point and mark
1626 ;; properly. If it fails, vc-restore-buffer-context
1627 ;; will give it a second try.
1628 (save-excursion
1629 (vc-call-backend backend 'clear-headers))
1630 (vc-restore-buffer-context context))
7849e179 1631 (set-buffer (find-file-noselect filename))
0e362f54
GM
1632 (vc-call-backend backend 'clear-headers)
1633 (kill-buffer filename)))))
c8de1d91 1634
b6909007 1635;;;###autoload
099bd78a
SM
1636(defun vc-merge ()
1637 "Merge changes between two versions into the current buffer's file.
1638This asks for two versions to merge from in the minibuffer. If the
1639first version is a branch number, then merge all changes from that
1640branch. If the first version is empty, merge news, i.e. recent changes
1641from the current branch.
0e362f54
GM
1642
1643See Info node `Merging'."
099bd78a 1644 (interactive)
ccb141e8
AS
1645 (vc-ensure-vc-buffer)
1646 (vc-buffer-sync)
1647 (let* ((file buffer-file-name)
1648 (backend (vc-backend file))
0e362f54 1649 (state (vc-state file))
099bd78a 1650 first-version second-version status)
0e362f54 1651 (cond
0e362f54
GM
1652 ((stringp state)
1653 (error "File is locked by %s" state))
1654 ((not (vc-editable-p file))
1655 (if (y-or-n-p
1656 "File must be checked out for merging. Check out now? ")
1657 (vc-checkout file t)
1658 (error "Merge aborted"))))
46e33aee 1659 (setq first-version
099bd78a
SM
1660 (read-string (concat "Branch or version to merge from "
1661 "(default: news on current branch): ")))
1662 (if (string= first-version "")
1663 (if (not (vc-find-backend-function backend 'merge-news))
1664 (error "Sorry, merging news is not implemented for %s" backend)
1665 (setq status (vc-call merge-news file)))
1666 (if (not (vc-find-backend-function backend 'merge))
1667 (error "Sorry, merging is not implemented for %s" backend)
1668 (if (not (vc-branch-p first-version))
46e33aee
TTN
1669 (setq second-version
1670 (read-string "Second version: "
099bd78a
SM
1671 (concat (vc-branch-part first-version) ".")))
1672 ;; We want to merge an entire branch. Set versions
1673 ;; accordingly, so that vc-BACKEND-merge understands us.
1674 (setq second-version first-version)
1675 ;; first-version must be the starting point of the branch
1676 (setq first-version (vc-branch-part first-version)))
1677 (setq status (vc-call merge file first-version second-version))))
1678 (vc-maybe-resolve-conflicts file status "WORKFILE" "MERGE SOURCE")))
0e362f54
GM
1679
1680(defun vc-maybe-resolve-conflicts (file status &optional name-A name-B)
1681 (vc-resynch-buffer file t (not (buffer-modified-p)))
1682 (if (zerop status) (message "Merge successful")
1683 (if (fboundp 'smerge-mode) (smerge-mode 1))
1684 (if (y-or-n-p "Conflicts detected. Resolve them now? ")
1685 (if (fboundp 'smerge-ediff)
1686 (smerge-ediff)
1687 (vc-resolve-conflicts name-A name-B))
1688 (message "File contains conflict markers"))))
ccb141e8 1689
beba4bd9
AS
1690(defvar vc-ediff-windows)
1691(defvar vc-ediff-result)
0e362f54
GM
1692(eval-when-compile
1693 (defvar ediff-buffer-A)
1694 (defvar ediff-buffer-B)
1695 (defvar ediff-buffer-C)
1696 (require 'ediff-util))
ccb141e8
AS
1697;;;###autoload
1698(defun vc-resolve-conflicts (&optional name-A name-B)
18483cf0
AS
1699 "Invoke ediff to resolve conflicts in the current buffer.
1700The conflicts must be marked with rcsmerge conflict markers."
1701 (interactive)
b6909007 1702 (vc-ensure-vc-buffer)
18483cf0
AS
1703 (let* ((found nil)
1704 (file-name (file-name-nondirectory buffer-file-name))
0e362f54
GM
1705 (your-buffer (generate-new-buffer
1706 (concat "*" file-name
ccb141e8 1707 " " (or name-A "WORKFILE") "*")))
0e362f54
GM
1708 (other-buffer (generate-new-buffer
1709 (concat "*" file-name
ccb141e8 1710 " " (or name-B "CHECKED-IN") "*")))
18483cf0 1711 (result-buffer (current-buffer)))
0e362f54 1712 (save-excursion
18483cf0
AS
1713 (set-buffer your-buffer)
1714 (erase-buffer)
1715 (insert-buffer result-buffer)
1716 (goto-char (point-min))
0e362f54 1717 (while (re-search-forward (concat "^<<<<<<< "
18483cf0
AS
1718 (regexp-quote file-name) "\n") nil t)
1719 (setq found t)
1720 (replace-match "")
1721 (if (not (re-search-forward "^=======\n" nil t))
1722 (error "Malformed conflict marker"))
1723 (replace-match "")
1724 (let ((start (point)))
1725 (if (not (re-search-forward "^>>>>>>> [0-9.]+\n" nil t))
1726 (error "Malformed conflict marker"))
1727 (delete-region start (point))))
1728 (if (not found)
1729 (progn
1730 (kill-buffer your-buffer)
1731 (kill-buffer other-buffer)
1732 (error "No conflict markers found")))
1733 (set-buffer other-buffer)
1734 (erase-buffer)
1735 (insert-buffer result-buffer)
1736 (goto-char (point-min))
0e362f54 1737 (while (re-search-forward (concat "^<<<<<<< "
18483cf0
AS
1738 (regexp-quote file-name) "\n") nil t)
1739 (let ((start (match-beginning 0)))
1740 (if (not (re-search-forward "^=======\n" nil t))
1741 (error "Malformed conflict marker"))
1742 (delete-region start (point))
1743 (if (not (re-search-forward "^>>>>>>> [0-9.]+\n" nil t))
1744 (error "Malformed conflict marker"))
1745 (replace-match "")))
1746 (let ((config (current-window-configuration))
1747 (ediff-default-variant 'default-B))
1748
1749 ;; Fire up ediff.
1750
1751 (set-buffer (ediff-merge-buffers your-buffer other-buffer))
1752
1753 ;; Ediff is now set up, and we are in the control buffer.
1754 ;; Do a few further adjustments and take precautions for exit.
1755
1756 (make-local-variable 'vc-ediff-windows)
1757 (setq vc-ediff-windows config)
1758 (make-local-variable 'vc-ediff-result)
0e362f54 1759 (setq vc-ediff-result result-buffer)
18483cf0 1760 (make-local-variable 'ediff-quit-hook)
6f41eeb5 1761 (setq ediff-quit-hook
0e362f54
GM
1762 (lambda ()
1763 (let ((buffer-A ediff-buffer-A)
1764 (buffer-B ediff-buffer-B)
1765 (buffer-C ediff-buffer-C)
1766 (result vc-ediff-result)
1767 (windows vc-ediff-windows))
1768 (ediff-cleanup-mess)
1769 (set-buffer result)
1770 (erase-buffer)
1771 (insert-buffer buffer-C)
1772 (kill-buffer buffer-A)
1773 (kill-buffer buffer-B)
1774 (kill-buffer buffer-C)
1775 (set-window-configuration windows)
1776 (message "Conflict resolution finished; you may save the buffer"))))
18483cf0
AS
1777 (message "Please resolve conflicts now; exit ediff when done")
1778 nil))))
1779
2f119435 1780;; The VC directory major mode. Coopt Dired for this.
e1f297e6
ER
1781;; All VC commands get mapped into logical equivalents.
1782
beba4bd9
AS
1783(defvar vc-dired-switches)
1784(defvar vc-dired-terse-mode)
1785
0e362f54
GM
1786(defvar vc-dired-mode-map
1787 (let ((map (make-sparse-keymap))
1788 (vmap (make-sparse-keymap)))
0e362f54 1789 (define-key map "\C-xv" vc-prefix-map)
099bd78a
SM
1790 ;; Emacs-20 has a lousy keymap inheritance that won't work here.
1791 ;; Emacs-21's is still lousy but just better enough that it'd work. -sm
1792 ;; (set-keymap-parent vmap vc-prefix-map)
1793 (setq vmap vc-prefix-map)
0e362f54 1794 (define-key map "v" vmap)
0e362f54
GM
1795 (define-key vmap "t" 'vc-dired-toggle-terse-mode)
1796 map))
1797
2f119435 1798(define-derived-mode vc-dired-mode dired-mode "Dired under VC"
0e362f54
GM
1799 "The major mode used in VC directory buffers.
1800
1801It works like Dired, but lists only files under version control, with
1802the current VC state of each file being indicated in the place of the
1803file's link count, owner, group and size. Subdirectories are also
1804listed, and you may insert them into the buffer as desired, like in
1805Dired.
1806
1807All Dired commands operate normally, with the exception of `v', which
1808is redefined as the version control prefix, so that you can type
3d30b8bc
RS
1809`vl', `v=' etc. to invoke `vc-print-log', `vc-diff', and the like on
1810the file named in the current Dired buffer line. `vv' invokes
1811`vc-next-action' on this file, or on all files currently marked.
1812There is a special command, `*l', to mark all files currently locked."
099bd78a
SM
1813 ;; define-derived-mode does it for us in Emacs-21, but not in Emacs-20.
1814 ;; We do it here because dired might not be loaded yet
1815 ;; when vc-dired-mode-map is initialized.
1816 (set-keymap-parent vc-dired-mode-map dired-mode-map)
421f0bfe
AS
1817 (make-local-hook 'dired-after-readin-hook)
1818 (add-hook 'dired-after-readin-hook 'vc-dired-hook nil t)
edcb979f
AS
1819 ;; The following is slightly modified from dired.el,
1820 ;; because file lines look a bit different in vc-dired-mode.
1821 (set (make-local-variable 'dired-move-to-filename-regexp)
0e362f54 1822 (let*
edcb979f
AS
1823 ((l "\\([A-Za-z]\\|[^\0-\177]\\)")
1824 ;; In some locales, month abbreviations are as short as 2 letters,
1825 ;; and they can be padded on the right with spaces.
1826 (month (concat l l "+ *"))
0e362f54 1827 ;; Recognize any non-ASCII character.
edcb979f
AS
1828 ;; The purpose is to match a Kanji character.
1829 (k "[^\0-\177]")
1830 ;; (k "[^\x00-\x7f\x80-\xff]")
1831 (s " ")
1832 (yyyy "[0-9][0-9][0-9][0-9]")
1833 (mm "[ 0-1][0-9]")
1834 (dd "[ 0-3][0-9]")
1835 (HH:MM "[ 0-2][0-9]:[0-5][0-9]")
1836 (western (concat "\\(" month s dd "\\|" dd s month "\\)"
61d6c25d 1837 s "\\(" HH:MM "\\|" s yyyy"\\|" yyyy s "\\)"))
edcb979f 1838 (japanese (concat mm k s dd k s "\\(" s HH:MM "\\|" yyyy k "\\)")))
0e362f54
GM
1839 ;; the .* below ensures that we find the last match on a line
1840 (concat ".*" s "\\(" western "\\|" japanese "\\)" s)))
a0019b45
AS
1841 (and (boundp 'vc-dired-switches)
1842 vc-dired-switches
1843 (set (make-local-variable 'dired-actual-switches)
1844 vc-dired-switches))
3b574573 1845 (set (make-local-variable 'vc-dired-terse-mode) vc-dired-terse-display)
2f119435
AS
1846 (setq vc-dired-mode t))
1847
3b574573
AS
1848(defun vc-dired-toggle-terse-mode ()
1849 "Toggle terse display in VC Dired."
1850 (interactive)
1851 (if (not vc-dired-mode)
1852 nil
1853 (setq vc-dired-terse-mode (not vc-dired-terse-mode))
1854 (if vc-dired-terse-mode
1855 (vc-dired-hook)
1856 (revert-buffer))))
1857
3d30b8bc
RS
1858(defun vc-dired-mark-locked ()
1859 "Mark all files currently locked."
1860 (interactive)
1861 (dired-mark-if (let ((f (dired-get-filename nil t)))
1862 (and f
1863 (not (file-directory-p f))
0e362f54 1864 (not (vc-up-to-date-p f))))
3d30b8bc
RS
1865 "locked file"))
1866
1867(define-key vc-dired-mode-map "*l" 'vc-dired-mark-locked)
1868
0e362f54
GM
1869(defun vc-default-dired-state-info (backend file)
1870 (let ((state (vc-state file)))
1871 (cond
1872 ((stringp state) (concat "(" state ")"))
1873 ((eq state 'edited) (concat "(" (vc-user-login-name) ")"))
1874 ((eq state 'needs-merge) "(merge)")
1875 ((eq state 'needs-patch) "(patch)")
1876 ((eq state 'unlocked-changes) "(stale)"))))
b0c9bc8c 1877
8c0aaf40 1878(defun vc-dired-reformat-line (x)
0e362f54
GM
1879 "Reformat a directory-listing line.
1880Replace various columns with version control information.
1881This code, like dired, assumes UNIX -l format."
3d30b8bc 1882 (beginning-of-line)
edcb979f 1883 (let ((pos (point)) limit perm date-and-file)
2f119435
AS
1884 (end-of-line)
1885 (setq limit (point))
1886 (goto-char pos)
edcb979f
AS
1887 (when
1888 (or
1889 (re-search-forward ;; owner and group
1890 "^\\(..[drwxlts-]+ \\) *[0-9]+ [^ ]+ +[^ ]+ +[0-9]+\\( .*\\)"
0e362f54 1891 limit t)
edcb979f 1892 (re-search-forward ;; only owner displayed
0e362f54 1893 "^\\(..[drwxlts-]+ \\) *[0-9]+ [^ ]+ +[0-9]+\\( .*\\)"
edcb979f
AS
1894 limit t)
1895 (re-search-forward ;; OS/2 -l format, no links, owner, group
1896 "^\\(..[drwxlts-]+ \\) *[0-9]+\\( .*\\)"
1897 limit t))
2f119435 1898 (setq perm (match-string 1)
edcb979f
AS
1899 date-and-file (match-string 2))
1900 (setq x (substring (concat x " ") 0 10))
1901 (replace-match (concat perm x date-and-file)))))
3d30b8bc
RS
1902
1903(defun vc-dired-hook ()
0e362f54
GM
1904 "Reformat the listing according to version control.
1905Called by dired after any portion of a vc-dired buffer has been read in."
3d30b8bc 1906 (message "Getting version information... ")
eccceb78 1907 (let (subdir filename (buffer-read-only nil) cvs-dir)
3d30b8bc 1908 (goto-char (point-min))
0e362f54
GM
1909 (while (not (eobp))
1910 (cond
3d30b8bc
RS
1911 ;; subdir header line
1912 ((setq subdir (dired-get-subdir))
0e362f54
GM
1913 ;; if the backend supports it, get the state
1914 ;; of all files in this directory at once
1915 (let ((backend (vc-responsible-backend subdir)))
1916 (if (vc-find-backend-function backend 'dir-state)
1917 (vc-call-backend backend 'dir-state subdir)))
3d30b8bc
RS
1918 (forward-line 1)
1919 ;; erase (but don't remove) the "total" line
0e362f54
GM
1920 (delete-region (point) (line-end-position))
1921 (beginning-of-line)
1922 (forward-line 1))
1923 ;; file line
3d30b8bc
RS
1924 ((setq filename (dired-get-filename nil t))
1925 (cond
3b574573 1926 ;; subdir
3d30b8bc 1927 ((file-directory-p filename)
0e362f54
GM
1928 (cond
1929 ((member (file-name-nondirectory filename)
3b574573
AS
1930 vc-directory-exclusion-list)
1931 (let ((pos (point)))
1932 (dired-kill-tree filename)
1933 (goto-char pos)
1934 (dired-kill-line)))
1935 (vc-dired-terse-mode
633cee46
AS
1936 ;; Don't show directories in terse mode. Don't use
1937 ;; dired-kill-line to remove it, because in recursive listings,
1938 ;; that would remove the directory contents as well.
0e362f54 1939 (delete-region (line-beginning-position)
633cee46 1940 (progn (forward-line 1) (point))))
3b574573
AS
1941 ((string-match "\\`\\.\\.?\\'" (file-name-nondirectory filename))
1942 (dired-kill-line))
1943 (t
3d30b8bc 1944 (vc-dired-reformat-line nil)
3b574573
AS
1945 (forward-line 1))))
1946 ;; ordinary file
0e362f54
GM
1947 ((and (vc-backend filename)
1948 (not (and vc-dired-terse-mode
1949 (vc-up-to-date-p filename))))
1950 (vc-dired-reformat-line (vc-call dired-state-info filename))
3d30b8bc 1951 (forward-line 1))
0e362f54 1952 (t
3d30b8bc
RS
1953 (dired-kill-line))))
1954 ;; any other line
3b574573
AS
1955 (t (forward-line 1))))
1956 (vc-dired-purge))
1957 (message "Getting version information... done")
1958 (save-restriction
1959 (widen)
633cee46
AS
1960 (cond ((eq (count-lines (point-min) (point-max)) 1)
1961 (goto-char (point-min))
1962 (message "No files locked under %s" default-directory)))))
3b574573
AS
1963
1964(defun vc-dired-purge ()
0e362f54 1965 "Remove empty subdirs."
3b574573
AS
1966 (let (subdir)
1967 (goto-char (point-min))
1968 (while (setq subdir (dired-get-subdir))
1969 (forward-line 2)
1970 (if (dired-get-filename nil t)
1971 (if (not (dired-next-subdir 1 t))
1972 (goto-char (point-max)))
1973 (forward-line -2)
1974 (if (not (string= (dired-current-directory) default-directory))
1975 (dired-do-kill-lines t "")
633cee46
AS
1976 ;; We cannot remove the top level directory.
1977 ;; Just make it look a little nicer.
1978 (forward-line 1)
1979 (kill-line)
3b574573
AS
1980 (if (not (dired-next-subdir 1 t))
1981 (goto-char (point-max))))))
1982 (goto-char (point-min))))
2f119435 1983
0e362f54
GM
1984(defun vc-dired-buffers-for-dir (dir)
1985 "Return a list of all vc-dired buffers that currently display DIR."
1986 (let (result)
099bd78a
SM
1987 ;; Check whether dired is loaded.
1988 (when (fboundp 'dired-buffers-for-dir)
1989 (mapcar (lambda (buffer)
1990 (with-current-buffer buffer
1991 (if vc-dired-mode
1992 (setq result (append result (list buffer))))))
1993 (dired-buffers-for-dir dir)))
0e362f54
GM
1994 result))
1995
1996(defun vc-dired-resynch-file (file)
1997 "Update the entries for FILE in any VC Dired buffers that list it."
1998 (let ((buffers (vc-dired-buffers-for-dir (file-name-directory file))))
1999 (when buffers
2000 (mapcar (lambda (buffer)
2001 (with-current-buffer buffer
2002 (if (dired-goto-file file)
2003 ;; bind vc-dired-terse-mode to nil so that
2004 ;; files won't vanish when they are checked in
2005 (let ((vc-dired-terse-mode nil))
2006 (dired-do-redisplay 1)))))
2007 buffers))))
2008
637a8ae9 2009;;;###autoload
0e362f54
GM
2010(defun vc-directory (dir read-switches)
2011 "Create a buffer in VC Dired Mode for directory DIR.
2012
2013See Info node `VC Dired Mode'.
2014
2015With prefix arg READ-SWITCHES, specify a value to override
2016`dired-listing-switches' when generating the listing."
2f119435 2017 (interactive "DDired under VC (directory): \nP")
0e362f54 2018 (let ((vc-dired-switches (concat vc-dired-listing-switches
3b574573 2019 (if vc-dired-recurse "R" ""))))
0e362f54 2020 (if read-switches
3b574573
AS
2021 (setq vc-dired-switches
2022 (read-string "Dired listing switches: "
2023 vc-dired-switches)))
3d30b8bc
RS
2024 (require 'dired)
2025 (require 'dired-aux)
0e362f54
GM
2026 (switch-to-buffer
2027 (dired-internal-noselect (expand-file-name (file-name-as-directory dir))
2028 vc-dired-switches
3d30b8bc 2029 'vc-dired-mode))))
e70bdc98 2030
594722a8
ER
2031
2032;; Named-configuration entry points
2033
0e362f54 2034(defun vc-snapshot-precondition (dir)
099bd78a
SM
2035 "Scan the tree below DIR, looking for non-uptodate files.
2036If any file is not up-to-date, return the name of the first such file.
2037\(This means, neither snapshot creation nor retrieval is allowed.\)
2038If one or more of the files are currently visited, return `visited'.
2039Otherwise, return nil."
503b5c85
RS
2040 (let ((status nil))
2041 (catch 'vc-locked-example
2042 (vc-file-tree-walk
0e362f54
GM
2043 dir
2044 (lambda (f)
2045 (if (not (vc-up-to-date-p f)) (throw 'vc-locked-example f)
2046 (if (get-file-buffer f) (setq status 'visited)))))
503b5c85 2047 status)))
594722a8 2048
637a8ae9 2049;;;###autoload
0e362f54 2050(defun vc-create-snapshot (dir name branchp)
6f41eeb5 2051 "Descending recursively from DIR, make a snapshot called NAME.
0e362f54
GM
2052For each registered file, the version level of its latest version
2053becomes part of the named configuration. If the prefix argument
2054BRANCHP is given, the snapshot is made as a new branch and the files
2055are checked out in that new branch."
2056 (interactive
2057 (list (read-file-name "Directory: " default-directory default-directory t)
2058 (read-string "New snapshot name: ")
2059 current-prefix-arg))
2060 (message "Making %s... " (if branchp "branch" "snapshot"))
2061 (if (file-directory-p dir) (setq dir (file-name-as-directory dir)))
2062 (vc-call-backend (vc-responsible-backend dir)
2063 'create-snapshot dir name branchp)
2064 (message "Making %s... done" (if branchp "branch" "snapshot")))
2065
2066(defun vc-default-create-snapshot (backend dir name branchp)
6f41eeb5 2067 (when branchp
0e362f54
GM
2068 (error "VC backend %s does not support module branches" backend))
2069 (let ((result (vc-snapshot-precondition dir)))
503b5c85 2070 (if (stringp result)
0e362f54 2071 (error "File %s is not up-to-date" result)
1dabb4e6 2072 (vc-file-tree-walk
0e362f54
GM
2073 dir
2074 (lambda (f)
2075 (vc-call assign-name f name))))))
594722a8 2076
637a8ae9 2077;;;###autoload
0e362f54 2078(defun vc-retrieve-snapshot (dir name)
099bd78a
SM
2079 "Descending recursively from DIR, retrieve the snapshot called NAME.
2080If NAME is empty, it refers to the latest versions.
2081If locking is used for the files in DIR, then there must not be any
2082locked files at or below DIR (but if NAME is empty, locked files are
2083allowed and simply skipped)."
0e362f54
GM
2084 (interactive
2085 (list (read-file-name "Directory: " default-directory default-directory t)
2086 (read-string "Snapshot name to retrieve (default latest versions): ")))
2087 (let ((update (yes-or-no-p "Update any affected buffers? "))
2088 (msg (if (or (not name) (string= name ""))
2089 (format "Updating %s... " (abbreviate-file-name dir))
2090 (format "Retrieving snapshot into %s... "
2091 (abbreviate-file-name dir)))))
2092 (message msg)
2093 (vc-call-backend (vc-responsible-backend dir)
2094 'retrieve-snapshot dir name update)
2095 (message (concat msg "done"))))
2096
2097(defun vc-default-retrieve-snapshot (backend dir name update)
2098 (if (string= name "")
2099 (progn
2100 (vc-file-tree-walk
2101 dir
2102 (lambda (f) (and
2103 (vc-up-to-date-p f)
2104 (vc-error-occurred
2105 (vc-call checkout f nil "")
2106 (if update (vc-resynch-buffer f t t)))))))
2107 (let ((result (vc-snapshot-precondition dir)))
2108 (if (stringp result)
2109 (error "File %s is locked" result)
2110 (setq update (and (eq result 'visited) update))
2111 (vc-file-tree-walk
2112 dir
2113 (lambda (f) (and
2114 (vc-error-occurred
2115 (vc-call checkout f nil name)
2116 (if update (vc-resynch-buffer f t t))))))))))
594722a8
ER
2117
2118;; Miscellaneous other entry points
2119
637a8ae9 2120;;;###autoload
594722a8
ER
2121(defun vc-print-log ()
2122 "List the change log of the current buffer in a window."
2123 (interactive)
b6909007
AS
2124 (vc-ensure-vc-buffer)
2125 (let ((file buffer-file-name))
0e362f54 2126 (vc-setup-buffer nil)
b6909007 2127 (setq default-directory (file-name-directory file))
0e362f54
GM
2128 (vc-call print-log file)
2129 (pop-to-buffer (current-buffer))
2130 (if (fboundp 'log-view-mode) (log-view-mode))
2131 (vc-exec-after
2132 `(progn
2133 (goto-char (point-max)) (forward-line -1)
2134 (while (looking-at "=*\n")
2135 (delete-char (- (match-end 0) (match-beginning 0)))
2136 (forward-line -1))
2137 (goto-char (point-min))
2138 (if (looking-at "[\b\t\n\v\f\r ]+")
2139 (delete-char (- (match-end 0) (match-beginning 0))))
2140 (shrink-window-if-larger-than-buffer)
2141 ;; move point to the log entry for the current version
2142 (if (fboundp 'log-view-goto-rev)
2143 (log-view-goto-rev ',(vc-workfile-version file))
2144 (if (vc-find-backend-function ',(vc-backend file) 'show-log-entry)
6f41eeb5
DL
2145 (vc-call-backend ',(vc-backend file)
2146 'show-log-entry
0e362f54 2147 ',(vc-workfile-version file))))))))
594722a8 2148
0ab66291
AS
2149(defun vc-default-comment-history (backend file)
2150 "Return a string with all log entries that were made under BACKEND for FILE."
2151 (if (vc-find-backend-function backend 'print-log)
2152 (with-temp-buffer
2153 (vc-call print-log file)
2154 (vc-call wash-log file)
2155 (buffer-string))))
2156
2157(defun vc-default-wash-log (backend file)
2158 "Remove all non-comment information from log output.
2159This default implementation works for RCS logs; backends should override
2160it if their logs are not in RCS format."
2161 (let ((separator (concat "^-+\nrevision [0-9.]+\ndate: .*\n"
2162 "\\(branches: .*;\n\\)?"
2163 "\\(\\*\\*\\* empty log message \\*\\*\\*\n\\)?")))
2164 (goto-char (point-max)) (forward-line -1)
2165 (while (looking-at "=*\n")
2166 (delete-char (- (match-end 0) (match-beginning 0)))
2167 (forward-line -1))
2168 (goto-char (point-min))
2169 (if (looking-at "[\b\t\n\v\f\r ]+")
2170 (delete-char (- (match-end 0) (match-beginning 0))))
2171 (goto-char (point-min))
2172 (re-search-forward separator nil t)
2173 (delete-region (point-min) (point))
2174 (while (re-search-forward separator nil t)
2175 (delete-region (match-beginning 0) (match-end 0)))))
2176
637a8ae9 2177;;;###autoload
594722a8 2178(defun vc-revert-buffer ()
18483cf0 2179 "Revert the current buffer's file back to the version it was based on.
9c95ac44 2180This asks for confirmation if the buffer contents are not identical
7849e179
SM
2181to that version. This function does not automatically pick up newer
2182changes found in the master file; use \\[universal-argument] \\[vc-next-action] to do so."
594722a8 2183 (interactive)
b6909007 2184 (vc-ensure-vc-buffer)
594722a8 2185 (let ((file buffer-file-name)
221cc4f4
RS
2186 ;; This operation should always ask for confirmation.
2187 (vc-suppress-confirm nil)
ffda0460
AS
2188 (obuf (current-buffer))
2189 status)
0e362f54 2190 (unless (vc-workfile-unchanged-p file)
a3255400
SM
2191 ;; vc-diff selects the new window, which is not what we want:
2192 ;; if the new window is on another frame, that'd require the user
2193 ;; moving her mouse to answer the yes-or-no-p question.
2194 (let ((win (save-selected-window
2195 (setq status (vc-diff nil t)) (selected-window))))
2196 (vc-exec-after `(message nil))
2197 (when status
2198 (unwind-protect
2199 (unless (yes-or-no-p "Discard changes? ")
ffda0460 2200 (error "Revert canceled"))
a3255400
SM
2201 (select-window win)
2202 (if (one-window-p t)
2203 (if (window-dedicated-p (selected-window))
2204 (make-frame-invisible))
2205 (delete-window))))))
751fa747 2206 (set-buffer obuf)
0e362f54
GM
2207 ;; Do the reverting
2208 (message "Reverting %s..." file)
045e1aa5 2209 (vc-revert-file file)
0e362f54 2210 (message "Reverting %s...done" file)))
594722a8 2211
ffda0460
AS
2212(defun vc-version-backup-file (file &optional rev)
2213 "If version backups should be used for FILE, and there exists
2214such a backup for REV or the current workfile version of file,
2215return the name of it; otherwise return nil."
10b48cc4 2216 (when (vc-call make-version-backups-p file)
ffda0460 2217 (let ((backup-file (vc-version-backup-file-name file rev)))
10b48cc4
AS
2218 (if (file-exists-p backup-file)
2219 backup-file
2220 ;; there is no automatic backup, but maybe the user made one manually
2221 (setq backup-file (vc-version-backup-file-name file rev 'manual))
2222 (if (file-exists-p backup-file)
2223 backup-file)))))
ffda0460 2224
045e1aa5
AS
2225(defun vc-revert-file (file)
2226 "Revert FILE back to the version it was based on."
045e1aa5
AS
2227 (with-vc-properties
2228 file
ffda0460
AS
2229 (let ((backup-file (vc-version-backup-file file)))
2230 (if (not backup-file)
2231 (vc-call revert file)
2232 (copy-file backup-file file 'ok-if-already-exists 'keep-date)
10b48cc4 2233 (vc-delete-automatic-version-backups file)))
a3255400
SM
2234 `((vc-state . up-to-date)
2235 (vc-checkout-time . ,(nth 5 (file-attributes file)))))
045e1aa5
AS
2236 (vc-resynch-buffer file t t))
2237
637a8ae9 2238;;;###autoload
594722a8 2239(defun vc-cancel-version (norevert)
34291cd2 2240 "Get rid of most recently checked in version of this file.
099bd78a 2241A prefix argument NOREVERT means do not revert the buffer afterwards."
594722a8 2242 (interactive "P")
b6909007 2243 (vc-ensure-vc-buffer)
099bd78a
SM
2244 (let* ((file (buffer-file-name))
2245 (backend (vc-backend file))
2246 (target (vc-workfile-version file))
7e48e092 2247 (config (current-window-configuration)) done)
0e362f54 2248 (cond
099bd78a 2249 ((not (vc-find-backend-function backend 'cancel-version))
0e362f54 2250 (error "Sorry, canceling versions is not supported under %s" backend))
099bd78a 2251 ((not (vc-call latest-on-branch-p file))
0e362f54 2252 (error "This is not the latest version; VC cannot cancel it"))
099bd78a 2253 ((not (vc-up-to-date-p file))
0e362f54 2254 (error (substitute-command-keys "File is not up to date; use \\[vc-revert-buffer] to discard changes"))))
7e48e092 2255 (if (null (yes-or-no-p (format "Remove version %s from master? " target)))
099bd78a 2256 (error "Aborted")
0e362f54
GM
2257 (setq norevert (or norevert (not
2258 (yes-or-no-p "Revert buffer to most recent remaining version? "))))
2259
099bd78a
SM
2260 (message "Removing last change from %s..." file)
2261 (with-vc-properties
2262 file
2263 (vc-call cancel-version file norevert)
a3255400 2264 `((vc-state . ,(if norevert 'edited 'up-to-date))
46e33aee
TTN
2265 (vc-checkout-time . ,(if norevert
2266 0
099bd78a 2267 (nth 5 (file-attributes file))))
a3255400 2268 (vc-workfile-version . nil)))
099bd78a
SM
2269 (message "Removing last change from %s...done" file)
2270
2271 (cond
2272 (norevert ;; clear version headers and mark the buffer modified
2273 (set-visited-file-name file)
2274 (when (not vc-make-backup-files)
2275 ;; inhibit backup for this buffer
2276 (make-local-variable 'backup-inhibited)
2277 (setq backup-inhibited t))
2278 (setq buffer-read-only nil)
2279 (vc-clear-headers)
2280 (vc-mode-line file)
2281 (vc-dired-resynch-file file))
2282 (t ;; revert buffer to file on disk
2283 (vc-resynch-buffer file t t)))
0e362f54
GM
2284 (message "Version %s has been removed from the master" target))))
2285
1d502d5a
AS
2286;;;autoload
2287(defun vc-switch-backend (file backend)
7849e179 2288 "Make BACKEND the current version control system for FILE.
1d502d5a
AS
2289FILE must already be registered in BACKEND. The change is not
2290permanent, only for the current session. This function only changes
7849e179
SM
2291VC's perspective on FILE, it does not register or unregister it.
2292By default, this command cycles through the registered backends.
2293To get a prompt, use a prefix argument."
2294 (interactive
1d502d5a
AS
2295 (list
2296 buffer-file-name
7849e179
SM
2297 (let ((backend (vc-backend buffer-file-name))
2298 (backends nil))
2299 ;; Find the registered backends.
2300 (dolist (backend vc-handled-backends)
2301 (when (vc-call-backend backend 'registered buffer-file-name)
2302 (push backend backends)))
2303 ;; Find the next backend.
ceec5a0c 2304 (let ((def (car (delq backend (append (memq backend backends) backends))))
7849e179
SM
2305 (others (delete backend backends)))
2306 (cond
2307 ((null others) (error "No other backend to switch to"))
2308 (current-prefix-arg
2309 (intern
2310 (upcase
2311 (completing-read
2312 (format "Switch to backend [%s]: " def)
2313 (mapcar (lambda (b) (list (downcase (symbol-name b)))) backends)
2314 nil t nil nil (downcase (symbol-name def))))))
2315 (t def))))))
ceec5a0c 2316 (unless (eq backend (vc-backend file))
ceec5a0c
SM
2317 (vc-file-clearprops file)
2318 (vc-file-setprop file 'vc-backend backend)
2319 ;; Force recomputation of the state
a3255400
SM
2320 (unless (vc-call-backend backend 'registered file)
2321 (vc-file-clearprops file)
2322 (error "%s is not registered in %s" file backend))
ceec5a0c 2323 (vc-mode-line file)))
1d502d5a 2324
1d502d5a
AS
2325;;;autoload
2326(defun vc-transfer-file (file new-backend)
ceec5a0c 2327 "Transfer FILE to another version control system NEW-BACKEND.
1d502d5a 2328If NEW-BACKEND has a higher precedence than FILE's current backend
ceec5a0c 2329\(i.e. it comes earlier in `vc-handled-backends'), then register FILE in
1d502d5a
AS
2330NEW-BACKEND, using the version number from the current backend as the
2331base level. If NEW-BACKEND has a lower precedence than the current
2332backend, then commit all changes that were made under the current
2333backend to NEW-BACKEND, and unregister FILE from the current backend.
2334\(If FILE is not yet registered under NEW-BACKEND, register it.)"
72cfc5fb
AS
2335 (let* ((old-backend (vc-backend file))
2336 (edited (memq (vc-state file) '(edited needs-merge)))
2337 (registered (vc-call-backend new-backend 'registered file))
2338 (move
2339 (and registered ; Never move if not registered in new-backend yet.
2340 ;; move if new-backend comes later in vc-handled-backends
2341 (or (memq new-backend (memq old-backend vc-handled-backends))
ffda0460 2342 (y-or-n-p "Final transfer? "))))
72cfc5fb 2343 (comment nil))
1d502d5a 2344 (if (eq old-backend new-backend)
72cfc5fb
AS
2345 (error "%s is the current backend of %s" new-backend file))
2346 (if registered
2347 (set-file-modes file (logior (file-modes file) 128))
2348 ;; `registered' might have switched under us.
2349 (vc-switch-backend file old-backend)
ffda0460
AS
2350 (let* ((rev (vc-workfile-version file))
2351 (modified-file (and edited (make-temp-name file)))
2352 (unmodified-file (and modified-file (vc-version-backup-file file))))
72cfc5fb
AS
2353 ;; Go back to the base unmodified file.
2354 (unwind-protect
2355 (progn
ffda0460
AS
2356 (when modified-file
2357 (copy-file file modified-file)
2358 ;; If we have a local copy of the unmodified file, handle that
2359 ;; here and not in vc-revert-file because we don't want to
2360 ;; delete that copy -- it is still useful for OLD-BACKEND.
2361 (if unmodified-file
2362 (copy-file unmodified-file file 'ok-if-already-exists)
2363 (if (y-or-n-p "Get base version from master? ")
2364 (vc-revert-file file))))
72cfc5fb 2365 (vc-call-backend new-backend 'receive-file file rev))
ffda0460 2366 (when modified-file
72cfc5fb
AS
2367 (vc-switch-backend file new-backend)
2368 (unless (eq (vc-checkout-model file) 'implicit)
2369 (vc-checkout file t nil))
ffda0460
AS
2370 (rename-file modified-file file 'ok-if-already-exists)
2371 (vc-file-setprop file 'vc-checkout-time nil)))))
72cfc5fb
AS
2372 (when move
2373 (vc-switch-backend file old-backend)
2374 (setq comment (vc-call comment-history file))
2375 (vc-call unregister file))
2376 (vc-switch-backend file new-backend)
2377 (when (or move edited)
1d502d5a 2378 (vc-file-setprop file 'vc-state 'edited)
ffda0460 2379 (vc-mode-line file)
0ab66291 2380 (vc-checkin file nil comment (stringp comment)))))
1d502d5a 2381
72cfc5fb
AS
2382(defun vc-default-unregister (backend file)
2383 "Default implementation of `vc-unregister', signals an error."
2384 (error "Unregistering files is not supported for %s" backend))
2385
2386(defun vc-default-receive-file (backend file rev)
2387 "Let BACKEND receive FILE from another version control system."
2388 (vc-call-backend backend 'register file rev ""))
2389
0e362f54
GM
2390(defun vc-rename-master (oldmaster newfile templates)
2391 "Rename OLDMASTER to be the master file for NEWFILE based on TEMPLATES."
2392 (let* ((dir (file-name-directory (expand-file-name oldmaster)))
2393 (newdir (or (file-name-directory newfile) ""))
2394 (newbase (file-name-nondirectory newfile))
2395 (masters
2396 ;; List of potential master files for `newfile'
2397 (mapcar
2398 (lambda (s) (vc-possible-master s newdir newbase))
2399 templates)))
2400 (if (or (file-symlink-p oldmaster)
2401 (file-symlink-p (file-name-directory oldmaster)))
2402 (error "This unsafe in the presence of symbolic links"))
2403 (rename-file
2404 oldmaster
2405 (catch 'found
2406 ;; If possible, keep the master file in the same directory.
2407 (mapcar (lambda (f)
2408 (if (and f (string= (file-name-directory (expand-file-name f))
2409 dir))
2410 (throw 'found f)))
2411 masters)
2412 ;; If not, just use the first possible place.
2413 (mapcar (lambda (f)
2414 (and f
2415 (or (not (setq dir (file-name-directory f)))
2416 (file-directory-p dir))
2417 (throw 'found f)))
2418 masters)
2419 (error "New file lacks a version control directory")))))
594722a8 2420
29fc1ce9 2421;;;###autoload
594722a8 2422(defun vc-rename-file (old new)
34291cd2
RS
2423 "Rename file OLD to NEW, and rename its master file likewise."
2424 (interactive "fVC rename file: \nFRename to: ")
80688f5c
RS
2425 ;; There are several ways of renaming files under CVS 1.3, but they all
2426 ;; have serious disadvantages. See the FAQ (available from think.com in
2427 ;; pub/cvs/). I'd rather send the user an error, than do something he might
2428 ;; consider to be wrong. When the famous, long-awaited rename database is
2429 ;; implemented things might change for the better. This is unlikely to occur
2430 ;; until CVS 2.0 is released. --ceder 1994-01-23 21:27:51
0e362f54
GM
2431 (let ((oldbuf (get-file-buffer old))
2432 (backend (vc-backend old)))
2433 (unless (or (null backend) (vc-find-backend-function backend 'rename-file))
2434 (error "Renaming files under %s is not supported in VC" backend))
d52f0de9 2435 (if (and oldbuf (buffer-modified-p oldbuf))
590cc449 2436 (error "Please save files before moving them"))
594722a8 2437 (if (get-file-buffer new)
590cc449 2438 (error "Already editing new file name"))
d52f0de9
RS
2439 (if (file-exists-p new)
2440 (error "New file already exists"))
0e362f54
GM
2441 (when backend
2442 (if (and backend (not (vc-up-to-date-p old)))
2443 (error "Please check in files before moving them"))
2444 (vc-call-backend backend 'rename-file old new))
2445 ;; Move the actual file (unless the backend did it already)
2446 (if (or (not backend) (file-exists-p old))
2447 (rename-file old new))
2448 ;; ?? Renaming a file might change its contents due to keyword expansion.
2449 ;; We should really check out a new copy if the old copy was precisely equal
2450 ;; to some checked in version. However, testing for this is tricky....
594722a8 2451 (if oldbuf
0e362f54 2452 (with-current-buffer oldbuf
4c145b9e
RS
2453 (let ((buffer-read-only buffer-read-only))
2454 (set-visited-file-name new))
2455 (vc-backend new)
2456 (vc-mode-line new)
0e362f54
GM
2457 (set-buffer-modified-p nil)))))
2458
2459;; Only defined in very recent Emacsen
2460(defvar small-temporary-file-directory nil)
594722a8 2461
637a8ae9 2462;;;###autoload
f35ecf88 2463(defun vc-update-change-log (&rest args)
0e362f54 2464 "Find change log file and add entries from recent version control logs.
d68e6990 2465Normally, find log entries for all registered files in the default
0e362f54 2466directory.
d68e6990 2467
099bd78a 2468With prefix arg of \\[universal-argument], only find log entries for the current buffer's file.
d68e6990
RS
2469
2470With any numeric prefix arg, find log entries for all currently visited
2471files that are under version control. This puts all the entries in the
2472log for the default directory, which may not be appropriate.
2473
099bd78a 2474From a program, any ARGS are assumed to be filenames for which
0e362f54 2475log entries should be gathered."
67242a23
RM
2476 (interactive
2477 (cond ((consp current-prefix-arg) ;C-u
2478 (list buffer-file-name))
2479 (current-prefix-arg ;Numeric argument.
2480 (let ((files nil)
2481 (buffers (buffer-list))
2482 file)
2483 (while buffers
2484 (setq file (buffer-file-name (car buffers)))
f3c61d82 2485 (and file (vc-backend file)
4b40fdea 2486 (setq files (cons file files)))
67242a23 2487 (setq buffers (cdr buffers)))
4b40fdea
PE
2488 files))
2489 (t
0e362f54
GM
2490 ;; Don't supply any filenames to backend; this means
2491 ;; it should find all relevant files relative to
2492 ;; the default-directory.
73a9679c 2493 nil)))
0e362f54
GM
2494 (vc-call-backend (vc-responsible-backend default-directory)
2495 'update-changelog args))
2496
2497(defun vc-default-update-changelog (backend files)
099bd78a
SM
2498 "Default implementation of update-changelog.
2499Uses `rcs2log' which only works for RCS and CVS."
0e362f54 2500 ;; FIXME: We (c|sh)ould add support for cvs2cl
449decf5 2501 (let ((odefault default-directory)
124c852b
RS
2502 (changelog (find-change-log))
2503 ;; Presumably not portable to non-Unixy systems, along with rcs2log:
0e362f54
GM
2504 (tempfile (funcall
2505 (if (fboundp 'make-temp-file) 'make-temp-file 'make-temp-name)
57c298c4
EZ
2506 (expand-file-name "vc"
2507 (or small-temporary-file-directory
2508 temporary-file-directory))))
b91916f3 2509 (full-name (or add-log-full-name
8172cd86
AS
2510 (user-full-name)
2511 (user-login-name)
2512 (format "uid%d" (number-to-string (user-uid)))))
b91916f3
RS
2513 (mailing-address (or add-log-mailing-address
2514 user-mail-address)))
124c852b 2515 (find-file-other-window changelog)
41dfb835
RS
2516 (barf-if-buffer-read-only)
2517 (vc-buffer-sync)
2518 (undo-boundary)
2519 (goto-char (point-min))
2520 (push-mark)
2521 (message "Computing change log entries...")
4b40fdea 2522 (message "Computing change log entries... %s"
124c852b
RS
2523 (unwind-protect
2524 (progn
0e362f54 2525 (setq default-directory odefault)
6f41eeb5
DL
2526 (if (eq 0 (apply 'call-process
2527 (expand-file-name "rcs2log"
2528 exec-directory)
0e362f54
GM
2529 nil (list t tempfile) nil
2530 "-c" changelog
2531 "-u" (concat (vc-user-login-name)
2532 "\t" full-name
2533 "\t" mailing-address)
2534 (mapcar
2535 (lambda (f)
2536 (file-relative-name
2537 (if (file-name-absolute-p f)
2538 f
2539 (concat odefault f))))
2540 files)))
2541 "done"
124c852b
RS
2542 (pop-to-buffer
2543 (set-buffer (get-buffer-create "*vc*")))
2544 (erase-buffer)
2545 (insert-file tempfile)
2546 "failed"))
0e362f54 2547 (setq default-directory (file-name-directory changelog))
124c852b 2548 (delete-file tempfile)))))
7d2d9482 2549
0e362f54 2550;;; Annotate functionality
7d2d9482 2551
f80f7bc2
RS
2552;; Declare globally instead of additional parameter to
2553;; temp-buffer-show-function (not possible to pass more than one
2554;; parameter).
099bd78a
SM
2555(defvar vc-annotate-ratio nil "Global variable.")
2556(defvar vc-annotate-backend nil "Global variable.")
0e362f54
GM
2557
2558(defun vc-annotate-get-backend (buffer)
099bd78a
SM
2559 "Return the backend matching \"Annotate\" buffer BUFFER.
2560Return NIL if no match made. Associations are made based on
0e362f54
GM
2561`vc-annotate-buffers'."
2562 (cdr (assoc buffer vc-annotate-buffers)))
7d2d9482 2563
0e362f54
GM
2564(define-derived-mode vc-annotate-mode fundamental-mode "Annotate"
2565 "Major mode for buffers displaying output from the `annotate' command.
7d2d9482
RS
2566
2567You can use the mode-specific menu to alter the time-span of the used
2568colors. See variable `vc-annotate-menu-elements' for customizing the
2569menu items."
7d2d9482
RS
2570 (vc-annotate-add-menu))
2571
2572(defun vc-annotate-display-default (&optional event)
2573 "Use the default color spectrum for VC Annotate mode."
0e362f54 2574 (interactive "e")
f80f7bc2 2575 (message "Redisplaying annotation...")
0e362f54 2576 (vc-annotate-display (current-buffer)
6f41eeb5 2577 nil
0e362f54 2578 (vc-annotate-get-backend (current-buffer)))
f80f7bc2 2579 (message "Redisplaying annotation...done"))
7d2d9482
RS
2580
2581(defun vc-annotate-add-menu ()
0e362f54
GM
2582 "Add the menu 'Annotate' to the menu bar in VC-Annotate mode."
2583 (setq vc-annotate-mode-menu (make-sparse-keymap "Annotate"))
2584 (define-key vc-annotate-mode-map [menu-bar vc-annotate-mode]
2585 (cons "VC-Annotate" vc-annotate-mode-menu))
7d2d9482
RS
2586 (define-key vc-annotate-mode-menu [default]
2587 '("Default" . vc-annotate-display-default))
2588 (let ((menu-elements vc-annotate-menu-elements))
2589 (while menu-elements
2590 (let* ((element (car menu-elements))
0e362f54
GM
2591 (days (round (* element
2592 (vc-annotate-car-last-cons vc-annotate-color-map)
7d2d9482
RS
2593 0.7585))))
2594 (setq menu-elements (cdr menu-elements))
2595 (define-key vc-annotate-mode-menu
2596 (vector days)
2597 (cons (format "Span %d days"
2598 days)
2599 `(lambda ()
2600 ,(format "Use colors spanning %d days" days)
f80f7bc2
RS
2601 (interactive)
2602 (message "Redisplaying annotation...")
2603 (vc-annotate-display
2604 (get-buffer (buffer-name))
0e362f54
GM
2605 (vc-annotate-time-span vc-annotate-color-map ,element)
2606 (vc-annotate-get-backend (current-buffer)))
f80f7bc2 2607 (message "Redisplaying annotation...done"))))))))
594722a8 2608
0e362f54
GM
2609
2610;;;; (defun vc-BACKEND-annotate-command (file buffer) ...)
2611;;;; Execute "annotate" on FILE by using `call-process' and insert
2612;;;; the contents in BUFFER.
2613
7d2d9482
RS
2614;;;###autoload
2615(defun vc-annotate (ratio)
0e362f54
GM
2616 "Display the result of the \"Annotate\" command using colors.
2617\"Annotate\" is defined by `vc-BACKEND-annotate-command'. New lines
2618are displayed in red, old in blue. A prefix argument specifies a
2619factor for stretching the time scale.
7d2d9482
RS
2620
2621`vc-annotate-menu-elements' customizes the menu elements of the
2622mode-specific menu. `vc-annotate-color-map' and
2623`vc-annotate-very-old-color' defines the mapping of time to
2624colors. `vc-annotate-background' specifies the background color."
2625 (interactive "p")
b6909007 2626 (vc-ensure-vc-buffer)
7d2d9482 2627 (message "Annotating...")
0e362f54 2628 (let ((temp-buffer-name (concat "*Annotate " (buffer-name) "*"))
7d2d9482 2629 (temp-buffer-show-function 'vc-annotate-display)
0e362f54
GM
2630 (vc-annotate-ratio ratio)
2631 (vc-annotate-backend (vc-backend (buffer-file-name))))
099bd78a
SM
2632 (if (not (vc-find-backend-function vc-annotate-backend 'annotate-command))
2633 (error "Sorry, annotating is not implemented for %s"
2634 vc-annotate-backend))
46e33aee 2635 (with-output-to-temp-buffer temp-buffer-name
0e362f54
GM
2636 (vc-call-backend vc-annotate-backend 'annotate-command
2637 (file-name-nondirectory (buffer-file-name))
2638 (get-buffer temp-buffer-name)))
2639 ;; Don't use the temp-buffer-name until the buffer is created
2640 ;; (only after `with-output-to-temp-buffer'.)
6f41eeb5 2641 (setq vc-annotate-buffers
0e362f54
GM
2642 (append vc-annotate-buffers
2643 (list (cons (get-buffer temp-buffer-name) vc-annotate-backend)))))
7d2d9482
RS
2644 (message "Annotating... done"))
2645
0e362f54 2646
f70419a8
RS
2647(defun vc-annotate-car-last-cons (a-list)
2648 "Return car of last cons in association list A-LIST."
2649 (if (not (eq nil (cdr a-list)))
2650 (vc-annotate-car-last-cons (cdr a-list))
2651 (car (car a-list))))
2652
2653(defun vc-annotate-time-span (a-list span &optional quantize)
6f41eeb5 2654 "Apply factor SPAN to the time-span of association list A-LIST.
0e362f54
GM
2655Return the new alist.
2656Optionally quantize to the factor of QUANTIZE."
7d2d9482 2657 ;; Apply span to each car of every cons
0e362f54 2658 (if (not (eq nil a-list))
f70419a8
RS
2659 (append (list (cons (* (car (car a-list)) span)
2660 (cdr (car a-list))))
0e362f54
GM
2661 (vc-annotate-time-span (nthcdr (or quantize ; optional
2662 1) ; Default to cdr
f70419a8
RS
2663 a-list) span quantize))))
2664
2665(defun vc-annotate-compcar (threshold a-list)
0e362f54
GM
2666 "Test successive cons cells of association list A-LIST against THRESHOLD.
2667Return the first cons cell which car is not less than THRESHOLD,
2668nil otherwise"
f70419a8
RS
2669 (let ((i 1)
2670 (tmp-cons (car a-list)))
2671 (while (and tmp-cons (< (car tmp-cons) threshold))
2672 (setq tmp-cons (car (nthcdr i a-list)))
2673 (setq i (+ i 1)))
2674 tmp-cons)) ; Return the appropriate value
2675
7d2d9482 2676
0e362f54
GM
2677;;;; (defun vc-BACKEND-annotate-difference (point) ...)
2678;;;;
2679;;;; Return the difference between the age of the line at point and
2680;;;; the current time. Return NIL if there is no more comparison to
2681;;;; be made in the buffer. Return value as defined for
2682;;;; `current-time'. You can safely assume that point is placed at
2683;;;; the beginning of each line, starting at `point-min'. The buffer
2684;;;; that point is placed in is the Annotate output, as defined by
2685;;;; the relevant backend.
2686
2687(defun vc-annotate-display (buffer &optional color-map backend)
099bd78a
SM
2688 "Do the VC-Annotate display in BUFFER using COLOR-MAP.
2689The original annotating file is supposed to be handled by BACKEND.
2690If BACKEND is NIL, variable VC-ANNOTATE-BACKEND is used instead.
2691This function is destructive on VC-ANNOTATE-BACKEND when BACKEND is non-nil."
7d2d9482 2692
f80f7bc2
RS
2693 ;; Handle the case of the global variable vc-annotate-ratio being
2694 ;; set. This variable is used to pass information from function
2695 ;; vc-annotate since it is not possible to use another parameter
0e362f54 2696 ;; (see temp-buffer-show-function).
7d2d9482 2697 (if (and (not color-map) vc-annotate-ratio)
f80f7bc2
RS
2698 ;; This will only be true if called from vc-annotate with ratio
2699 ;; being non-nil.
2700 (setq color-map (vc-annotate-time-span vc-annotate-color-map
2701 vc-annotate-ratio)))
0e362f54
GM
2702 (set-buffer buffer)
2703 (display-buffer buffer)
2704 (if (not vc-annotate-mode) ; Turn on vc-annotate-mode if not done
2705 (vc-annotate-mode))
2706 (goto-char (point-min)) ; Position at the top of the buffer.
2707 ;; Delete old overlays
2708 (mapcar
2709 (lambda (overlay)
2710 (if (overlay-get overlay 'vc-annotation)
2711 (delete-overlay overlay)))
2712 (overlays-in (point-min) (point-max)))
2713 (goto-char (point-min)) ; Position at the top of the buffer.
2714
2715 (if backend (setq vc-annotate-backend backend)) ; Destructive on `vc-annotate-backend'
2716
2717 (let ((difference (vc-call-backend vc-annotate-backend 'annotate-difference (point))))
2718 (while difference
2719 (let*
2720 ((color (or (vc-annotate-compcar
2721 difference (or color-map vc-annotate-color-map))
2722 (cons nil vc-annotate-very-old-color)))
2723 ;; substring from index 1 to remove any leading `#' in the name
2724 (face-name (concat "vc-annotate-face-" (substring (cdr color) 1)))
2725 ;; Make the face if not done.
2726 (face (or (intern-soft face-name)
2727 (let ((tmp-face (make-face (intern face-name))))
2728 (set-face-foreground tmp-face (cdr color))
2729 (if vc-annotate-background
2730 (set-face-background tmp-face vc-annotate-background))
2731 tmp-face))) ; Return the face
2732 (point (point))
2733 overlay)
f70419a8 2734 (forward-line 1)
05dad1e6
AS
2735 (setq overlay (make-overlay point (point)))
2736 (overlay-put overlay 'face face)
0e362f54
GM
2737 (overlay-put overlay 'vc-annotation t))
2738 (setq difference (vc-call-backend vc-annotate-backend 'annotate-difference (point))))))
f70419a8 2739
7d2d9482 2740\f
c6d4f628 2741;; Collect back-end-dependent stuff here
594722a8 2742
0e362f54 2743(defalias 'vc-default-logentry-check 'ignore)
594722a8 2744
594722a8
ER
2745(defun vc-check-headers ()
2746 "Check if the current file has any headers in it."
2747 (interactive)
0e362f54 2748 (vc-call-backend (vc-backend buffer-file-name) 'check-headers))
594722a8
ER
2749
2750;; Back-end-dependent stuff ends here.
2751
2752;; Set up key bindings for use while editing log messages
2753
099bd78a 2754(define-derived-mode vc-log-mode text-mode "VC-Log"
0e362f54 2755 "Major mode for editing VC log entries.
594722a8
ER
2756These bindings are added to the global keymap when you enter this mode:
2757\\[vc-next-action] perform next logical version-control operation on current file
0e362f54 2758\\[vc-register] register current file
594722a8
ER
2759\\[vc-toggle-read-only] like next-action, but won't register files
2760\\[vc-insert-headers] insert version-control headers in current file
2761\\[vc-print-log] display change history of current file
2762\\[vc-revert-buffer] revert buffer to latest version
2763\\[vc-cancel-version] undo latest checkin
2764\\[vc-diff] show diffs between file versions
f1818994 2765\\[vc-version-other-window] visit old version in another window
594722a8 2766\\[vc-directory] show all files locked by any user in or below .
0e362f54 2767\\[vc-annotate] colorful display of the cvs annotate command
594722a8
ER
2768\\[vc-update-change-log] add change log entry from recent checkins
2769
2770While you are entering a change log message for a version, the following
2771additional bindings will be in effect.
2772
2773\\[vc-finish-logentry] proceed with check in, ending log message entry
2774
2775Whenever you do a checkin, your log comment is added to a ring of
2776saved comments. These can be recalled as follows:
2777
2778\\[vc-next-comment] replace region with next message in comment ring
2779\\[vc-previous-comment] replace region with previous message in comment ring
8c0aaf40
ER
2780\\[vc-comment-search-reverse] search backward for regexp in the comment ring
2781\\[vc-comment-search-forward] search backward for regexp in the comment ring
594722a8 2782
0e362f54
GM
2783Entry to the change-log submode calls the value of `text-mode-hook', then
2784the value of `vc-log-mode-hook'.
594722a8
ER
2785
2786Global user options:
0e362f54 2787 `vc-initial-comment' If non-nil, require user to enter a change
594722a8
ER
2788 comment upon first checkin of the file.
2789
0e362f54 2790 `vc-keep-workfiles' Non-nil value prevents workfiles from being
594722a8
ER
2791 deleted when changes are checked in
2792
0e362f54 2793 `vc-suppress-confirm' Suppresses some confirmation prompts,
594722a8
ER
2794 notably for reversions.
2795
0e362f54 2796 vc-BACKEND-header Which keywords to insert when adding headers
594722a8 2797 with \\[vc-insert-headers]. Defaults to
0e362f54 2798 '(\"\%\W\%\") under SCCS, '(\"\$Id\$\") under
80688f5c 2799 RCS and CVS.
594722a8 2800
0e362f54 2801 `vc-static-header-alist' By default, version headers inserted in C files
594722a8 2802 get stuffed in a static string area so that
80688f5c
RS
2803 ident(RCS/CVS) or what(SCCS) can see them in
2804 the compiled object code. You can override
2805 this by setting this variable to nil, or change
594722a8
ER
2806 the header template by changing it.
2807
0e362f54 2808 `vc-command-messages' if non-nil, display run messages from the
594722a8
ER
2809 actual version-control utilities (this is
2810 intended primarily for people hacking vc
099bd78a
SM
2811 itself)."
2812 (make-local-variable 'vc-comment-ring-index))
0e362f54
GM
2813
2814(defun vc-log-edit (file)
099bd78a
SM
2815 "Set up `log-edit' for use with VC on FILE.
2816If `log-edit' is not available, resort to `vc-log-mode'."
2817 (setq default-directory
2818 (if file (file-name-directory file)
2819 (with-current-buffer vc-parent-buffer default-directory)))
2820 (if (fboundp 'log-edit)
2821 (log-edit 'vc-finish-logentry nil
2822 (if file `(lambda () ',(list (file-name-nondirectory file)))
2823 ;; If FILE is nil, we were called from vc-dired.
2824 (lambda ()
2825 (with-current-buffer vc-parent-buffer
2826 (dired-get-marked-files t)))))
2827 (vc-log-mode))
0e362f54
GM
2828 (set (make-local-variable 'vc-log-file) file)
2829 (make-local-variable 'vc-log-version)
099bd78a 2830 (set-buffer-modified-p nil)
0e362f54 2831 (setq buffer-file-name nil))
594722a8
ER
2832
2833;;; These things should probably be generally available
2834
2f119435
AS
2835(defun vc-file-tree-walk (dirname func &rest args)
2836 "Walk recursively through DIRNAME.
0e362f54 2837Invoke FUNC f ARGS on each VC-managed file f underneath it."
2f119435
AS
2838 (vc-file-tree-walk-internal (expand-file-name dirname) func args)
2839 (message "Traversing directory %s...done" dirname))
02da6253
PE
2840
2841(defun vc-file-tree-walk-internal (file func args)
2842 (if (not (file-directory-p file))
0e362f54 2843 (if (vc-backend file) (apply func file args))
993a1a44 2844 (message "Traversing directory %s..." (abbreviate-file-name file))
02da6253
PE
2845 (let ((dir (file-name-as-directory file)))
2846 (mapcar
0e362f54
GM
2847 (lambda (f) (or
2848 (string-equal f ".")
2849 (string-equal f "..")
2850 (member f vc-directory-exclusion-list)
2851 (let ((dirf (expand-file-name f dir)))
2852 (or
2853 (file-symlink-p dirf);; Avoid possible loops
2854 (vc-file-tree-walk-internal dirf func args)))))
02da6253 2855 (directory-files dir)))))
594722a8
ER
2856
2857(provide 'vc)
2858
2859;;; DEVELOPER'S NOTES ON CONCURRENCY PROBLEMS IN THIS CODE
2860;;;
2861;;; These may be useful to anyone who has to debug or extend the package.
c6d4f628
RS
2862;;; (Note that this information corresponds to versions 5.x. Some of it
2863;;; might have been invalidated by the additions to support branching
2864;;; and RCS keyword lookup. AS, 1995/03/24)
0e362f54 2865;;;
594722a8
ER
2866;;; A fundamental problem in VC is that there are time windows between
2867;;; vc-next-action's computations of the file's version-control state and
2868;;; the actions that change it. This is a window open to lossage in a
2869;;; multi-user environment; someone else could nip in and change the state
2870;;; of the master during it.
0e362f54 2871;;;
594722a8
ER
2872;;; The performance problem is that rlog/prs calls are very expensive; we want
2873;;; to avoid them as much as possible.
0e362f54 2874;;;
594722a8 2875;;; ANALYSIS:
0e362f54 2876;;;
594722a8 2877;;; The performance problem, it turns out, simplifies in practice to the
0e362f54 2878;;; problem of making vc-state fast. The two other functions that call
594722a8
ER
2879;;; prs/rlog will not be so commonly used that the slowdown is a problem; one
2880;;; makes snapshots, the other deletes the calling user's last change in the
2881;;; master.
0e362f54 2882;;;
594722a8
ER
2883;;; The race condition implies that we have to either (a) lock the master
2884;;; during the entire execution of vc-next-action, or (b) detect and
2885;;; recover from errors resulting from dispatch on an out-of-date state.
0e362f54 2886;;;
a7acbbe4 2887;;; Alternative (a) appears to be infeasible. The problem is that we can't
594722a8
ER
2888;;; guarantee that the lock will ever be removed. Suppose a user starts a
2889;;; checkin, the change message buffer pops up, and the user, having wandered
2890;;; off to do something else, simply forgets about it?
0e362f54 2891;;;
594722a8 2892;;; Alternative (b), on the other hand, works well with a cheap way to speed up
0e362f54 2893;;; vc-state. Usually, if a file is registered, we can read its locked/
594722a8 2894;;; unlocked state and its current owner from its permissions.
0e362f54 2895;;;
594722a8
ER
2896;;; This shortcut will fail if someone has manually changed the workfile's
2897;;; permissions; also if developers are munging the workfile in several
2898;;; directories, with symlinks to a master (in this latter case, the
2899;;; permissions shortcut will fail to detect a lock asserted from another
2900;;; directory).
0e362f54 2901;;;
594722a8
ER
2902;;; Note that these cases correspond exactly to the errors which could happen
2903;;; because of a competing checkin/checkout race in between two instances of
2904;;; vc-next-action.
0e362f54 2905;;;
594722a8 2906;;; For VC's purposes, a workfile/master pair may have the following states:
0e362f54 2907;;;
594722a8 2908;;; A. Unregistered. There is a workfile, there is no master.
0e362f54 2909;;;
594722a8 2910;;; B. Registered and not locked by anyone.
0e362f54 2911;;;
594722a8 2912;;; C. Locked by calling user and unchanged.
0e362f54 2913;;;
594722a8 2914;;; D. Locked by the calling user and changed.
0e362f54 2915;;;
594722a8 2916;;; E. Locked by someone other than the calling user.
0e362f54 2917;;;
594722a8 2918;;; This makes for 25 states and 20 error conditions. Here's the matrix:
0e362f54 2919;;;
594722a8
ER
2920;;; VC's idea of state
2921;;; |
2922;;; V Actual state RCS action SCCS action Effect
2923;;; A B C D E
2924;;; A . 1 2 3 4 ci -u -t- admin -fb -i<file> initial admin
2925;;; B 5 . 6 7 8 co -l get -e checkout
2926;;; C 9 10 . 11 12 co -u unget; get revert
2927;;; D 13 14 15 . 16 ci -u -m<comment> delta -y<comment>; get checkin
b0c9bc8c 2928;;; E 17 18 19 20 . rcs -u -M -l unget -n ; get -g steal lock
0e362f54 2929;;;
594722a8 2930;;; All commands take the master file name as a last argument (not shown).
0e362f54 2931;;;
594722a8
ER
2932;;; In the discussion below, a "self-race" is a pathological situation in
2933;;; which VC operations are being attempted simultaneously by two or more
2934;;; Emacsen running under the same username.
0e362f54 2935;;;
594722a8 2936;;; The vc-next-action code has the following windows:
0e362f54 2937;;;
594722a8
ER
2938;;; Window P:
2939;;; Between the check for existence of a master file and the call to
2940;;; admin/checkin in vc-buffer-admin (apparent state A). This window may
2941;;; never close if the initial-comment feature is on.
0e362f54 2942;;;
594722a8
ER
2943;;; Window Q:
2944;;; Between the call to vc-workfile-unchanged-p in and the immediately
2945;;; following revert (apparent state C).
0e362f54 2946;;;
594722a8
ER
2947;;; Window R:
2948;;; Between the call to vc-workfile-unchanged-p in and the following
2949;;; checkin (apparent state D). This window may never close.
0e362f54 2950;;;
594722a8
ER
2951;;; Window S:
2952;;; Between the unlock and the immediately following checkout during a
2953;;; revert operation (apparent state C). Included in window Q.
0e362f54 2954;;;
594722a8 2955;;; Window T:
0e362f54
GM
2956;;; Between vc-state and the following checkout (apparent state B).
2957;;;
594722a8 2958;;; Window U:
0e362f54 2959;;; Between vc-state and the following revert (apparent state C).
594722a8 2960;;; Includes windows Q and S.
0e362f54 2961;;;
594722a8 2962;;; Window V:
0e362f54 2963;;; Between vc-state and the following checkin (apparent state
594722a8
ER
2964;;; D). This window may never be closed if the user fails to complete the
2965;;; checkin message. Includes window R.
0e362f54 2966;;;
594722a8 2967;;; Window W:
0e362f54 2968;;; Between vc-state and the following steal-lock (apparent
34291cd2 2969;;; state E). This window may never close if the user fails to complete
594722a8 2970;;; the steal-lock message. Includes window X.
0e362f54 2971;;;
594722a8
ER
2972;;; Window X:
2973;;; Between the unlock and the immediately following re-lock during a
0e362f54 2974;;; steal-lock operation (apparent state E). This window may never close
594722a8 2975;;; if the user fails to complete the steal-lock message.
0e362f54 2976;;;
594722a8 2977;;; Errors:
0e362f54 2978;;;
594722a8
ER
2979;;; Apparent state A ---
2980;;;
2981;;; 1. File looked unregistered but is actually registered and not locked.
0e362f54 2982;;;
594722a8
ER
2983;;; Potential cause: someone else's admin during window P, with
2984;;; caller's admin happening before their checkout.
0e362f54 2985;;;
b0c9bc8c
AS
2986;;; RCS: Prior to version 5.6.4, ci fails with message
2987;;; "no lock set by <user>". From 5.6.4 onwards, VC uses the new
2988;;; ci -i option and the message is "<file>,v: already exists".
594722a8 2989;;; SCCS: admin will fail with error (ad19).
0e362f54 2990;;;
594722a8 2991;;; We can let these errors be passed up to the user.
0e362f54 2992;;;
594722a8 2993;;; 2. File looked unregistered but is actually locked by caller, unchanged.
0e362f54 2994;;;
594722a8 2995;;; Potential cause: self-race during window P.
0e362f54 2996;;;
b0c9bc8c
AS
2997;;; RCS: Prior to version 5.6.4, reverts the file to the last saved
2998;;; version and unlocks it. From 5.6.4 onwards, VC uses the new
2999;;; ci -i option, failing with message "<file>,v: already exists".
594722a8 3000;;; SCCS: will fail with error (ad19).
0e362f54 3001;;;
594722a8 3002;;; Either of these consequences is acceptable.
0e362f54 3003;;;
594722a8 3004;;; 3. File looked unregistered but is actually locked by caller, changed.
0e362f54 3005;;;
594722a8 3006;;; Potential cause: self-race during window P.
0e362f54
GM
3007;;;
3008;;; RCS: Prior to version 5.6.4, VC registers the caller's workfile as
3009;;; a delta with a null change comment (the -t- switch will be
b0c9bc8c
AS
3010;;; ignored). From 5.6.4 onwards, VC uses the new ci -i option,
3011;;; failing with message "<file>,v: already exists".
594722a8 3012;;; SCCS: will fail with error (ad19).
0e362f54 3013;;;
594722a8 3014;;; 4. File looked unregistered but is locked by someone else.
0e362f54 3015;;;
594722a8
ER
3016;;; Potential cause: someone else's admin during window P, with
3017;;; caller's admin happening *after* their checkout.
0e362f54
GM
3018;;;
3019;;; RCS: Prior to version 5.6.4, ci fails with a
3020;;; "no lock set by <user>" message. From 5.6.4 onwards,
3021;;; VC uses the new ci -i option, failing with message
b0c9bc8c 3022;;; "<file>,v: already exists".
594722a8 3023;;; SCCS: will fail with error (ad19).
0e362f54 3024;;;
594722a8 3025;;; We can let these errors be passed up to the user.
0e362f54 3026;;;
594722a8
ER
3027;;; Apparent state B ---
3028;;;
3029;;; 5. File looked registered and not locked, but is actually unregistered.
0e362f54 3030;;;
594722a8 3031;;; Potential cause: master file got nuked during window P.
0e362f54 3032;;;
594722a8
ER
3033;;; RCS: will fail with "RCS/<file>: No such file or directory"
3034;;; SCCS: will fail with error ut4.
0e362f54 3035;;;
594722a8 3036;;; We can let these errors be passed up to the user.
0e362f54 3037;;;
594722a8
ER
3038;;; 6. File looked registered and not locked, but is actually locked by the
3039;;; calling user and unchanged.
0e362f54 3040;;;
594722a8 3041;;; Potential cause: self-race during window T.
0e362f54 3042;;;
594722a8
ER
3043;;; RCS: in the same directory as the previous workfile, co -l will fail
3044;;; with "co error: writable foo exists; checkout aborted". In any other
3045;;; directory, checkout will succeed.
3046;;; SCCS: will fail with ge17.
0e362f54 3047;;;
594722a8 3048;;; Either of these consequences is acceptable.
0e362f54 3049;;;
594722a8
ER
3050;;; 7. File looked registered and not locked, but is actually locked by the
3051;;; calling user and changed.
0e362f54 3052;;;
594722a8 3053;;; As case 6.
0e362f54 3054;;;
594722a8
ER
3055;;; 8. File looked registered and not locked, but is actually locked by another
3056;;; user.
0e362f54 3057;;;
594722a8 3058;;; Potential cause: someone else checks it out during window T.
0e362f54 3059;;;
594722a8
ER
3060;;; RCS: co error: revision 1.3 already locked by <user>
3061;;; SCCS: fails with ge4 (in directory) or ut7 (outside it).
0e362f54 3062;;;
594722a8 3063;;; We can let these errors be passed up to the user.
0e362f54 3064;;;
594722a8
ER
3065;;; Apparent state C ---
3066;;;
3067;;; 9. File looks locked by calling user and unchanged, but is unregistered.
0e362f54 3068;;;
594722a8 3069;;; As case 5.
0e362f54 3070;;;
594722a8
ER
3071;;; 10. File looks locked by calling user and unchanged, but is actually not
3072;;; locked.
0e362f54 3073;;;
594722a8
ER
3074;;; Potential cause: a self-race in window U, or by the revert's
3075;;; landing during window X of some other user's steal-lock or window S
3076;;; of another user's revert.
0e362f54 3077;;;
594722a8
ER
3078;;; RCS: succeeds, refreshing the file from the identical version in
3079;;; the master.
3080;;; SCCS: fails with error ut4 (p file nonexistent).
3081;;;
3082;;; Either of these consequences is acceptable.
0e362f54 3083;;;
594722a8
ER
3084;;; 11. File is locked by calling user. It looks unchanged, but is actually
3085;;; changed.
0e362f54 3086;;;
594722a8
ER
3087;;; Potential cause: the file would have to be touched by a self-race
3088;;; during window Q.
0e362f54 3089;;;
594722a8
ER
3090;;; The revert will succeed, removing whatever changes came with
3091;;; the touch. It is theoretically possible that work could be lost.
0e362f54 3092;;;
594722a8
ER
3093;;; 12. File looks like it's locked by the calling user and unchanged, but
3094;;; it's actually locked by someone else.
0e362f54 3095;;;
594722a8 3096;;; Potential cause: a steal-lock in window V.
0e362f54 3097;;;
594722a8
ER
3098;;; RCS: co error: revision <rev> locked by <user>; use co -r or rcs -u
3099;;; SCCS: fails with error un2
0e362f54 3100;;;
594722a8 3101;;; We can pass these errors up to the user.
0e362f54 3102;;;
594722a8
ER
3103;;; Apparent state D ---
3104;;;
3105;;; 13. File looks like it's locked by the calling user and changed, but it's
3106;;; actually unregistered.
0e362f54 3107;;;
594722a8 3108;;; Potential cause: master file got nuked during window P.
0e362f54
GM
3109;;;
3110;;; RCS: Prior to version 5.6.4, checks in the user's version as an
b0c9bc8c
AS
3111;;; initial delta. From 5.6.4 onwards, VC uses the new ci -j
3112;;; option, failing with message "no such file or directory".
594722a8
ER
3113;;; SCCS: will fail with error ut4.
3114;;;
b0c9bc8c
AS
3115;;; This case is kind of nasty. Under RCS prior to version 5.6.4,
3116;;; VC may fail to detect the loss of previous version information.
0e362f54 3117;;;
594722a8
ER
3118;;; 14. File looks like it's locked by the calling user and changed, but it's
3119;;; actually unlocked.
0e362f54 3120;;;
594722a8
ER
3121;;; Potential cause: self-race in window V, or the checkin happening
3122;;; during the window X of someone else's steal-lock or window S of
3123;;; someone else's revert.
0e362f54 3124;;;
594722a8
ER
3125;;; RCS: ci will fail with "no lock set by <user>".
3126;;; SCCS: delta will fail with error ut4.
0e362f54 3127;;;
594722a8
ER
3128;;; 15. File looks like it's locked by the calling user and changed, but it's
3129;;; actually locked by the calling user and unchanged.
0e362f54 3130;;;
594722a8
ER
3131;;; Potential cause: another self-race --- a whole checkin/checkout
3132;;; sequence by the calling user would have to land in window R.
0e362f54 3133;;;
594722a8
ER
3134;;; SCCS: checks in a redundant delta and leaves the file unlocked as usual.
3135;;; RCS: reverts to the file state as of the second user's checkin, leaving
3136;;; the file unlocked.
3137;;;
3138;;; It is theoretically possible that work could be lost under RCS.
0e362f54 3139;;;
594722a8
ER
3140;;; 16. File looks like it's locked by the calling user and changed, but it's
3141;;; actually locked by a different user.
0e362f54 3142;;;
594722a8
ER
3143;;; RCS: ci error: no lock set by <user>
3144;;; SCCS: unget will fail with error un2
0e362f54 3145;;;
594722a8 3146;;; We can pass these errors up to the user.
0e362f54 3147;;;
594722a8
ER
3148;;; Apparent state E ---
3149;;;
3150;;; 17. File looks like it's locked by some other user, but it's actually
3151;;; unregistered.
0e362f54 3152;;;
594722a8 3153;;; As case 13.
0e362f54 3154;;;
594722a8
ER
3155;;; 18. File looks like it's locked by some other user, but it's actually
3156;;; unlocked.
0e362f54 3157;;;
594722a8 3158;;; Potential cause: someone released a lock during window W.
0e362f54 3159;;;
594722a8
ER
3160;;; RCS: The calling user will get the lock on the file.
3161;;; SCCS: unget -n will fail with cm4.
0e362f54 3162;;;
594722a8 3163;;; Either of these consequences will be OK.
0e362f54 3164;;;
594722a8
ER
3165;;; 19. File looks like it's locked by some other user, but it's actually
3166;;; locked by the calling user and unchanged.
0e362f54 3167;;;
594722a8
ER
3168;;; Potential cause: the other user relinquishing a lock followed by
3169;;; a self-race, both in window W.
0e362f54 3170;;;
594722a8
ER
3171;;; Under both RCS and SCCS, both unlock and lock will succeed, making
3172;;; the sequence a no-op.
0e362f54 3173;;;
594722a8
ER
3174;;; 20. File looks like it's locked by some other user, but it's actually
3175;;; locked by the calling user and changed.
0e362f54 3176;;;
594722a8 3177;;; As case 19.
0e362f54 3178;;;
594722a8 3179;;; PROBLEM CASES:
0e362f54 3180;;;
594722a8 3181;;; In order of decreasing severity:
0e362f54 3182;;;
b0c9bc8c 3183;;; Cases 11 and 15 are the only ones that potentially lose work.
594722a8 3184;;; They would require a self-race for this to happen.
0e362f54 3185;;;
594722a8
ER
3186;;; Case 13 in RCS loses information about previous deltas, retaining
3187;;; only the information in the current workfile. This can only happen
3188;;; if the master file gets nuked in window P.
0e362f54 3189;;;
594722a8
ER
3190;;; Case 3 in RCS and case 15 under SCCS insert a redundant delta with
3191;;; no change comment in the master. This would require a self-race in
3192;;; window P or R respectively.
0e362f54 3193;;;
594722a8 3194;;; Cases 2, 10, 19 and 20 do extra work, but make no changes.
0e362f54 3195;;;
594722a8
ER
3196;;; Unfortunately, it appears to me that no recovery is possible in these
3197;;; cases. They don't yield error messages, so there's no way to tell that
3198;;; a race condition has occurred.
0e362f54 3199;;;
594722a8
ER
3200;;; All other cases don't change either the workfile or the master, and
3201;;; trigger command errors which the user will see.
0e362f54 3202;;;
594722a8
ER
3203;;; Thus, there is no explicit recovery code.
3204
3205;;; vc.el ends here