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