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