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