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