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