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