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