;;; vc.el --- drive a version-control system from within Emacs
-;; Copyright (C) 1992,93,94,95,96,97,98,2000 Free Software Foundation, Inc.
+;; Copyright (C) 1992,93,94,95,96,97,98,2000,2001 Free Software Foundation, Inc.
;; Author: FSF (see below for full credits)
;; Maintainer: Andre Spiegel <spiegel@gnu.org>
-;; $Id: vc.el,v 1.283 2000/10/26 12:38:02 fx Exp $
+;; $Id: vc.el,v 1.303 2001/07/30 15:01:49 spiegel Exp $
;; This file is part of GNU Emacs.
;;
;; Developer's notes on some concurrency issues are included at the end of
;; the file.
-
-;;; Code:
-
-;;;;;;;;;;;;;;;;; Backend-specific functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
-;; for each operation FUN, the backend should provide a function vc-BACKEND-FUN.
-;; Operations marked with a `-' instead of a `*' are optional.
-
+;; ADDING SUPPORT FOR OTHER BACKENDS
+;;
+;; VC can use arbitrary version control systems as a backend. To add
+;; support for a new backend named SYS, write a library vc-sys.el that
+;; contains functions of the form `vc-sys-...' (note that SYS is in lower
+;; case for the function and library names). VC will use that library if
+;; you put the symbol SYS somewhere into the list of
+;; `vc-handled-backends'. Then, for example, if `vc-sys-registered'
+;; returns non-nil for a file, all SYS-specific versions of VC commands
+;; will be available for that file.
+;;
+;; VC keeps some per-file information in the form of properties (see
+;; vc-file-set/getprop in vc-hooks.el). The backend-specific functions
+;; do not generally need to be aware of these properties. For example,
+;; `vc-sys-workfile-version' should compute the workfile version and
+;; return it; it should not look it up in the property, and it needn't
+;; store it there either. However, if a backend-specific function does
+;; store a value in a property, that value takes precedence over any
+;; value that the generic code might want to set (check for uses of
+;; the macro `with-vc-properties' in vc.el).
+;;
+;; In the list of functions below, each identifier needs to be prepended
+;; with `vc-sys-'. Some of the functions are mandatory (marked with a
+;; `*'), others are optional (`-').
+;;
+;; STATE-QUERYING FUNCTIONS
+;;
;; * registered (file)
-;; * state (file)
+;;
+;; Return non-nil if FILE is registered in this backend.
+;;
+;; * state (file)
+;;
+;; Return the current version control state of FILE. For a list of
+;; possible values, see `vc-state'. This function should do a full and
+;; reliable state computation; it is usually called immediately after
+;; C-x v v. If you want to use a faster heuristic when visiting a
+;; file, put that into `state-heuristic' below.
+;;
;; - state-heuristic (file)
-;; The default behavior delegates to `state'.
+;;
+;; If provided, this function is used to estimate the version control
+;; state of FILE at visiting time. It should be considerably faster
+;; than the implementation of `state'. For a list of possible values,
+;; see the doc string of `vc-state'.
+;;
;; - dir-state (dir)
+;;
+;; If provided, this function is used to find the version control state
+;; of all files in DIR in a fast way. The function should not return
+;; anything, but rather store the files' states into the corresponding
+;; `vc-state' properties.
+;;
+;; * workfile-version (file)
+;;
+;; Return the current workfile version of FILE.
+;;
+;; - latest-on-branch-p (file)
+;;
+;; Return non-nil if the current workfile version of FILE is the latest
+;; on its branch. The default implementation always returns t, which
+;; means that working with non-current versions is not supported by
+;; default.
+;;
;; * checkout-model (file)
+;;
+;; Indicate whether FILE needs to be "checked out" before it can be
+;; edited. See `vc-checkout-model' for a list of possible values.
+;;
+;; - workfile-unchanged-p (file)
+;;
+;; Return non-nil if FILE is unchanged from its current workfile
+;; version. This function should do a brief comparison of FILE's
+;; contents with those of the master version. If the backend does not
+;; have such a brief-comparison feature, the default implementation of
+;; this function can be used, which delegates to a full
+;; vc-BACKEND-diff.
+;;
;; - mode-line-string (file)
-;; * workfile-version (file)
-;; * revert (file)
-;; - merge-news (file)
-;; Only needed if state `needs-merge' is possible.
-;; - merge (file rev1 rev2)
-;; - steal-lock (file &optional version)
-;; Only required if files can be locked by somebody else.
-;; * register (file rev comment)
-;; * unregister (file backend)
-;; - receive-file (file rev)
+;;
+;; If provided, this function should return the VC-specific mode line
+;; string for FILE. The default implementation deals well with all
+;; states that `vc-state' can return.
+;;
+;; - dired-state-info (file)
+;;
+;; Translate the `vc-state' property of FILE into a string that can be
+;; used in a vc-dired buffer. The default implementation deals well
+;; with all states that `vc-state' can return.
+;;
+;; STATE-CHANGING FUNCTIONS
+;;
+;; * register (file &optional rev comment)
+;;
+;; Register FILE in this backend. Optionally, an initial revision REV
+;; and an initial description of the file, COMMENT, may be specified.
+;;
;; - responsible-p (file)
-;; Should also work if FILE is a directory (ends with a slash).
+;;
+;; Return non-nil if this backend considers itself "responsible" for
+;; FILE, which can also be a directory. This function is used to find
+;; out what backend to use for registration of new files and for things
+;; like change log generation. The default implementation always
+;; returns nil.
+;;
;; - could-register (file)
-;; * checkout (file writable &optional rev destfile)
-;; Checkout revision REV of FILE into DESTFILE.
-;; DESTFILE defaults to FILE.
-;; The file should be made writable if WRITABLE is non-nil.
-;; REV can be nil (BASE) or "" (HEAD) or any other revision.
+;;
+;; Return non-nil if FILE could be registered under this backend. The
+;; default implementation always returns t.
+;;
+;; - receive-file (file rev)
+;;
+;; Let this backend "receive" a file that is already registered under
+;; another backend. The default implementation simply calls `register'
+;; for FILE, but it can be overridden to do something more specific,
+;; e.g. keep revision numbers consistent or choose editing modes for
+;; FILE that resemble those of the other backend.
+;;
+;; - unregister (file)
+;;
+;; Unregister FILE from this backend. This is only needed if this
+;; backend may be used as a "more local" backend for temporary editing.
+;;
;; * checkin (file rev comment)
-;; - logentry-check ()
-;; * diff (file &optional rev1 rev2)
-;; Insert the diff for FILE into the current buffer.
-;; REV1 should default to workfile-version.
-;; REV2 should default to the current workfile
-;; Return a status of either 0 (i.e. no diff) or 1 (i.e. either non-empty
-;; diff or the diff is run asynchronously).
-;; - workfile-unchanged-p (file)
-;; Return non-nil if FILE is unchanged from its current workfile version.
-;; This function should do a brief comparison of FILE's contents
-;; with those of the master version. If the backend does not have
-;; such a brief-comparison feature, the default implementation of this
-;; function can be used, which delegates to a full vc-BACKEND-diff.
-;; - clear-headers ()
-;; * check-headers ()
-;; - dired-state-info (file)
-;; - create-snapshot (dir name branchp)
-;; Take a snapshot of the current state of files under DIR and name it NAME.
-;; This should make sure that files are up-to-date before proceeding
-;; with the action.
-;; DIR can also be a file and if BRANCHP is specified, NAME
-;; should be created as a branch and DIR should be checked out under
-;; this new branch. The default behavior does not support branches
-;; but does a sanity check, a tree traversal and for each file calls
-;; `assign-name'.
-;; * assign-name (file name)
-;; Give name NAME to the current version of FILE, assuming it is
-;; up-to-date. Only used by the default version of `create-snapshot'.
-;; - retrieve-snapshot (dir name update)
-;; Retrieve a named snapshot of all registered files at or below DIR.
-;; If UPDATE is non-nil, then update buffers of any files in the snapshot
-;; that are currently visited.
+;;
+;; Commit changes in FILE to this backend. If REV is non-nil, that
+;; should become the new revision number. COMMENT is used as a
+;; check-in comment.
+;;
+;; * checkout (file &optional editable rev destfile)
+;;
+;; Check out revision REV of FILE into the working area. If EDITABLE
+;; is non-nil, FILE should be writable by the user and if locking is
+;; used for FILE, a lock should also be set. If REV is non-nil, that
+;; is the revision to check out (default is current workfile version);
+;; if REV is the empty string, that means to check out the head of the
+;; trunk. If optional arg DESTFILE is given, it is an alternate
+;; filename to write the contents to.
+;;
+;; * revert (file)
+;;
+;; Revert FILE back to the current workfile version.
+;;
+;; - cancel-version (file editable)
+;;
+;; Cancel the current workfile version of FILE, i.e. remove it from the
+;; master. EDITABLE non-nil means that FILE should be writable
+;; afterwards, and if locking is used for FILE, then a lock should also
+;; be set. If this function is not provided, trying to cancel a
+;; version is caught as an error.
+;;
+;; - merge (file rev1 rev2)
+;;
+;; Merge the changes between REV1 and REV2 into the current working file.
+;;
+;; - merge-news (file)
+;;
+;; Merge recent changes from the current branch into FILE.
+;;
+;; - steal-lock (file &optional version)
+;;
+;; Steal any lock on the current workfile version of FILE, or on
+;; VERSION if that is provided. This function is only needed if
+;; locking is used for files under this backend, and if files can
+;; indeed be locked by other users.
+;;
+;; HISTORY FUNCTIONS
+;;
;; * print-log (file)
-;; Insert the revision log of FILE into the current buffer.
+;;
+;; Insert the revision log of FILE into the *vc* buffer.
+;;
;; - show-log-entry (version)
+;;
+;; If provided, search the log entry for VERSION in the current buffer,
+;; and make sure it is displayed in the buffer's window. The default
+;; implementation of this function works for RCS-style logs.
+;;
;; - wash-log (file)
-;; Remove all non-comment information from the output of print-log
+;;
+;; Remove all non-comment information from the output of print-log. The
+;; default implementation of this function works for RCS-style logs.
+;;
+;; - logentry-check ()
+;;
+;; If defined, this function is run to find out whether the user
+;; entered a valid log entry for check-in. The log entry is in the
+;; current buffer, and if it is not a valid one, the function should
+;; throw an error.
+;;
;; - comment-history (file)
+;;
+;; Return a string containing all log entries that were made for FILE.
+;; This is used for transferring a file from one backend to another,
+;; retaining comment information. The default implementation of this
+;; function does this by calling print-log and then wash-log, and
+;; returning the resulting buffer contents as a string.
+;;
;; - update-changelog (files)
-;; Find changelog entries for FILES, or for all files at or below
-;; the default-directory if FILES is nil.
-;; * latest-on-branch-p (file)
-;; - cancel-version (file writable)
+;;
+;; Using recent log entries, create ChangeLog entries for FILES, or for
+;; all files at or below the default-directory if FILES is nil. The
+;; default implementation runs rcs2log, which handles RCS- and
+;; CVS-style logs.
+;;
+;; * diff (file &optional rev1 rev2)
+;;
+;; Insert the diff for FILE into the *vc-diff* buffer. If REV1 and REV2
+;; are non-nil, report differences from REV1 to REV2. If REV1 is nil,
+;; use the current workfile version (as found in the repository) as the
+;; older version; if REV2 is nil, use the current workfile contents as
+;; the newer version. This function should return a status of either 0
+;; (no differences found), or 1 (either non-empty diff or the diff is
+;; run asynchronously).
+;;
+;; - annotate-command (file buf rev)
+;;
+;; If this function is provided, it should produce an annotated version
+;; of FILE in BUF, relative to version REV. This is currently only
+;; implemented for CVS, using the `cvs annotate' command.
+;;
+;; - annotate-difference (point)
+;;
+;; Only required if `annotate-command' is defined for the backend.
+;; Return the difference between the age of the line at point and the
+;; current time. Return NIL if there is no more comparison to be made
+;; in the buffer. Return value as defined for `current-time'. You can
+;; safely assume that point is placed at the beginning of each line,
+;; starting at `point-min'. The buffer that point is placed in is the
+;; Annotate output, as defined by the relevant backend.
+;;
+;; SNAPSHOT SYSTEM
+;;
+;; - create-snapshot (dir name branchp)
+;;
+;; Take a snapshot of the current state of files under DIR and name it
+;; NAME. This should make sure that files are up-to-date before
+;; proceeding with the action. DIR can also be a file and if BRANCHP
+;; is specified, NAME should be created as a branch and DIR should be
+;; checked out under this new branch. The default implementation does
+;; not support branches but does a sanity check, a tree traversal and
+;; for each file calls `assign-name'.
+;;
+;; - assign-name (file name)
+;;
+;; Give name NAME to the current version of FILE, assuming it is
+;; up-to-date. Only used by the default version of `create-snapshot'.
+;;
+;; - retrieve-snapshot (dir name update)
+;;
+;; Retrieve a named snapshot of all registered files at or below DIR.
+;; If UPDATE is non-nil, then update buffers of any files in the
+;; snapshot that are currently visited. The default implementation
+;; does a sanity check whether there aren't any uncommitted changes at
+;; or below DIR, and then performs a tree walk, using the `checkout'
+;; function to retrieve the corresponding versions.
+;;
+;; MISCELLANEOUS
+;;
+;; - make-version-backups-p (file)
+;;
+;; Return non-nil if unmodified repository versions of FILE should be
+;; backed up locally. If this is done, VC can perform `diff' and
+;; `revert' operations itself, without calling the backend system. The
+;; default implementation always returns nil.
+;;
+;; - check-headers ()
+;;
+;; Return non-nil if the current buffer contains any version headers.
+;;
+;; - clear-headers ()
+;;
+;; In the current buffer, reset all version headers to their unexpanded
+;; form. This function should be provided if the state-querying code
+;; for this backend uses the version headers to determine the state of
+;; a file. This function will then be called whenever VC changes the
+;; version control state in such a way that the headers would give
+;; wrong information.
+;;
;; - rename-file (old new)
-;; - annotate-command (file buf)
-;; - annotate-difference (pos)
-;; Only required if `annotate-command' is defined for the backend.
+;;
+;; Rename file OLD to NEW, both in the working area and in the
+;; repository. If this function is not provided, the command
+;; `vc-rename-file' will signal an error.
+
+;;; Code:
(require 'vc-hooks)
(require 'ring)
(defconst vc-maximum-comment-ring-size 32
"Maximum number of saved comments in the comment ring.")
-;;; This is duplicated in diff.el.
+;; This is duplicated in diff.el.
(defvar diff-switches "-c"
"*A string or list of strings specifying switches to be passed to diff.")
+(defcustom vc-diff-switches nil
+ "*A string or list of strings specifying switches for diff under VC.
+There is also an option vc-BACKEND-diff-switches for each BACKEND that
+VC can handle."
+ :type '(choice (const :tag "None" nil)
+ (string :tag "Argument String")
+ (repeat :tag "Argument List"
+ :value ("")
+ string))
+ :group 'vc
+ :version "21.1")
+
+;;;###autoload
+(defcustom vc-checkout-hook nil
+ "*Normal hook (list of functions) run after a file has been checked out.
+See `run-hooks'."
+ :type 'hook
+ :group 'vc
+ :version "21.1")
+
;;;###autoload
(defcustom vc-checkin-hook nil
"*Normal hook (list of functions) run after a checkin is done.
:group 'vc)
\f
-;;; The main keymap
-
-(defvar vc-prefix-map
- (let ((map (make-sparse-keymap)))
- (define-key map "a" 'vc-update-change-log)
- (define-key map "b" 'vc-switch-backend)
- (define-key map "c" 'vc-cancel-version)
- (define-key map "d" 'vc-directory)
- (define-key map "g" 'vc-annotate)
- (define-key map "h" 'vc-insert-headers)
- (define-key map "i" 'vc-register)
- (define-key map "l" 'vc-print-log)
- (define-key map "m" 'vc-merge)
- (define-key map "r" 'vc-retrieve-snapshot)
- (define-key map "s" 'vc-create-snapshot)
- (define-key map "u" 'vc-revert-buffer)
- (define-key map "v" 'vc-next-action)
- (define-key map "=" 'vc-diff)
- (define-key map "~" 'vc-version-other-window)
- map))
-(fset 'vc-prefix-map vc-prefix-map)
+;; The main keymap
;; Initialization code, to be done just once at load-time
(defvar vc-log-mode-map
(defvar vc-comment-ring-index nil)
(defvar vc-last-comment-match "")
-;;; functions that operate on RCS revision numbers. This code should
-;;; also be moved into the backends. It stays for now, however, since
-;;; it is used in code below.
+;; functions that operate on RCS revision numbers. This code should
+;; also be moved into the backends. It stays for now, however, since
+;; it is used in code below.
(defun vc-trunk-p (rev)
"Return t if REV is a revision on the trunk."
(not (eq nil (string-match "\\`[0-9]+\\.[0-9]+\\'" rev))))
(or (eq (vc-checkout-model file) 'implicit)
(memq (vc-state file) '(edited needs-merge))))
-;;; Two macros for elisp programming
+;; Two macros for elisp programming
;;;###autoload
(defmacro with-vc-file (file comment &rest body)
"Check out a writable copy of FILE if necessary and execute the body.
Each function is called inside the buffer in which the command was run
and is passed 3 argument: the COMMAND, the FILE and the FLAGS.")
+;;;###autoload
(defun vc-do-command (buffer okstatus command file &rest flags)
"Execute a version control command, notifying user and checking for errors.
-Output from COMMAND goes to BUFFER, or *vc* if BUFFER is nil or the current
-buffer (which is assumed to be properly setup) if BUFFER is t. The
-command is considered successful if its exit status does not exceed
-OKSTATUS (if OKSTATUS is nil, that means to ignore errors, if it is 'async,
-that means not to wait for termination of the subprocess). FILE is
-the name of the working file (may also be nil, to execute commands
-that don't expect a file name). If an optional list of FLAGS is present,
+Output from COMMAND goes to BUFFER, or *vc* if BUFFER is nil or the
+current buffer if BUFFER is t. If the destination buffer is not
+already current, set it up properly and erase it. The command is
+considered successful if its exit status does not exceed OKSTATUS (if
+OKSTATUS is nil, that means to ignore errors, if it is 'async, that
+means not to wait for termination of the subprocess). FILE is the
+name of the working file (may also be nil, to execute commands that
+don't expect a file name). If an optional list of FLAGS is present,
that is inserted into the command line before the filename."
(and file (setq file (expand-file-name file)))
(if vc-command-messages
(message "Running %s on %s..." command file))
(save-current-buffer
- (unless (eq buffer t) (vc-setup-buffer buffer))
+ (unless (or (eq buffer t)
+ (and (stringp buffer)
+ (string= (buffer-name) buffer))
+ (eq buffer (current-buffer)))
+ (vc-setup-buffer buffer))
(let ((squeezed nil)
(inhibit-read-only t)
(status 0))
(if (eq okstatus 'async)
(let ((proc (apply 'start-process command (current-buffer) command
squeezed)))
- (message "Running %s in the background..." command)
+ (unless (active-minibuffer-window)
+ (message "Running %s in the background..." command))
;;(set-process-sentinel proc (lambda (p msg) (delete-process p)))
(set-process-filter proc 'vc-process-filter)
(vc-exec-after
- `(message "Running %s in the background... done" ',command)))
+ `(unless (active-minibuffer-window)
+ (message "Running %s in the background... done" ',command))))
(setq status (apply 'call-process command nil t nil squeezed))
(when (or (not (integerp status)) (and okstatus (< okstatus status)))
(pop-to-buffer (current-buffer))
(vc-file-setprop file 'vc-checkout-time (if unchanged lastmod 0))
unchanged))))
-(defun vc-default-workfile-unchanged-p (file)
+(defun vc-default-workfile-unchanged-p (backend file)
"Default check whether FILE is unchanged: diff against master version."
(zerop (vc-call diff file (vc-workfile-version file))))
+(defun vc-default-latest-on-branch-p (backend file)
+ "Default check whether the current workfile version of FILE is the
+latest on its branch."
+ t)
+
(defun vc-recompute-state (file)
"Force a recomputation of the version control state of FILE.
The state is computed using the exact, and possibly expensive
(message "%s is up-to-date" file))))
;; Abnormal: edited but read-only
- ((and visited (eq state 'edited) buffer-read-only)
+ ((and visited (eq state 'edited)
+ buffer-read-only (not (file-writable-p file)))
;; Make the file+buffer read-write. If the user really wanted to
;; commit, he'll get a chance to do that next time around, anyway.
(message "File is edited but read-only; making it writable")
(vc-next-action-on-file buffer-file-name verbose)
(error "Buffer %s is not associated with a file" (buffer-name)))))
-;;; These functions help the vc-next-action entry point
+;; These functions help the vc-next-action entry point
;;;###autoload
(defun vc-register (&optional set-version comment)
(defun vc-checkout (file &optional writable rev)
"Retrieve a copy of the revision REV of FILE.
If WRITABLE is non-nil, make sure the retrieved file is writable.
-REV defaults to the latest revision."
+REV defaults to the latest revision.
+
+After check-out, runs the normal hook `vc-checkout-hook'."
(and writable
(not rev)
(vc-call make-version-backups-p file)
'needs-patch)
'edited))
(vc-checkout-time . ,(nth 5 (file-attributes file)))))
- (vc-resynch-buffer file t t))
+ (vc-resynch-buffer file t t)
+ (run-hooks 'vc-checkout-hook))
(defun vc-steal-lock (file rev owner)
"Steal the lock on FILE."
rel2-default ") ")
"Newer version (default: current source): ")
nil nil rel2-default))))
- (vc-setup-buffer "*vc-diff*")
(if (file-directory-p file)
;; recursive directory diff
- (let ((inhibit-read-only t))
+ (progn
+ (vc-setup-buffer "*vc-diff*")
(if (string-equal rel1 "") (setq rel1 nil))
(if (string-equal rel2 "") (setq rel2 nil))
- (insert "Diffs between "
- (or rel1 "last version checked in")
- " and "
- (or rel2 "current workfile(s)")
- ":\n\n")
+ (let ((inhibit-read-only t))
+ (insert "Diffs between "
+ (or rel1 "last version checked in")
+ " and "
+ (or rel2 "current workfile(s)")
+ ":\n\n"))
(setq default-directory (file-name-as-directory file))
;; FIXME: this should do a single exec in CVS.
(vc-file-tree-walk
file
(vc-version-backup-file file rel2))))
(if (and file-rel1 file-rel2)
- (apply 'vc-do-command t 1 "diff" nil
+ (apply 'vc-do-command "*vc-diff*" 1 "diff" nil
(append (if (listp diff-switches)
diff-switches
(list diff-switches))
+ (if (listp vc-diff-switches)
+ vc-diff-switches
+ (list vc-diff-switches))
(list (file-relative-name file-rel1)
(file-relative-name file-rel2))))
- (cd (file-name-directory file))
(vc-call diff file rel1 rel2))))
+ (set-buffer "*vc-diff*")
(if (and (zerop (buffer-size))
(not (get-buffer-process (current-buffer))))
(progn
;; Gnus-5.8.5 sets up an autoload for diff-mode, even if it's
;; not available. Work around that.
(if (require 'diff-mode nil t) (diff-mode))
- (vc-exec-after '(progn (if (eq (buffer-size) 0)
- (insert "No differences found.\n"))
- (goto-char (point-min))
- (shrink-window-if-larger-than-buffer)))
+ (vc-exec-after '(let ((inhibit-read-only t))
+ (if (eq (buffer-size) 0)
+ (insert "No differences found.\n"))
+ (goto-char (point-min))
+ (shrink-window-if-larger-than-buffer)))
t))
+(defmacro vc-diff-switches-list (backend)
+ "Make a list of `diff-switches', `vc-diff-switches',
+and `vc-BACKEND-diff-switches'."
+ `(append
+ (if (listp diff-switches) diff-switches (list diff-switches))
+ (if (listp vc-diff-switches) vc-diff-switches (list vc-diff-switches))
+ (let ((backend-switches
+ (eval (intern (concat "vc-" (symbol-name ',backend)
+ "-diff-switches")))))
+ (if (listp backend-switches) backend-switches (list backend-switches)))))
+
;;;###autoload
(defun vc-version-other-window (rev)
"Visit version REV of the current buffer in another window.
(manual-backup (vc-version-backup-file-name file version 'manual)))
(unless (file-exists-p manual-backup)
(if (file-exists-p automatic-backup)
- (copy-file automatic-backup manual-backup nil 'keep-date)
+ (rename-file automatic-backup manual-backup nil)
(vc-call checkout file nil version manual-backup)))
(find-file-other-window manual-backup)))
(defvar vc-dired-mode-map
(let ((map (make-sparse-keymap))
(vmap (make-sparse-keymap)))
- (define-key map "\C-xv" vc-prefix-map)
- ;; Emacs-20 has a lousy keymap inheritance that won't work here.
- ;; Emacs-21's is still lousy but just better enough that it'd work. -sm
- ;; (set-keymap-parent vmap vc-prefix-map)
- (setq vmap vc-prefix-map)
+ (define-key map "\C-xv" vmap)
(define-key map "v" vmap)
+ (set-keymap-parent vmap vc-prefix-map)
(define-key vmap "t" 'vc-dired-toggle-terse-mode)
map))
(setq update (and (eq result 'visited) update))
(vc-file-tree-walk
dir
- (lambda (f) (and
- (vc-error-occurred
- (vc-call checkout f nil name)
- (if update (vc-resynch-buffer f t t))))))))))
+ (lambda (f) (vc-error-occurred
+ (vc-call checkout f nil name)
+ (if update (vc-resynch-buffer f t t)))))))))
;; Miscellaneous other entry points
(interactive)
(vc-ensure-vc-buffer)
(let ((file buffer-file-name))
- (vc-setup-buffer nil)
- (setq default-directory (file-name-directory file))
(vc-call print-log file)
+ (set-buffer "*vc*")
(pop-to-buffer (current-buffer))
(if (fboundp 'log-view-mode) (log-view-mode))
(vc-exec-after
(vc-suppress-confirm nil)
(obuf (current-buffer))
status)
+ (if (vc-up-to-date-p file)
+ (unless (yes-or-no-p "File seems up-to-date. Revert anyway? ")
+ (error "Revert canceled")))
(unless (vc-workfile-unchanged-p file)
;; vc-diff selects the new window, which is not what we want:
;; if the new window is on another frame, that'd require the user
(message "Reverting %s...done" file)))
(defun vc-version-backup-file (file &optional rev)
- "If version backups should be used for FILE, and there exists
+ "Return name of backup file for revision REV of FILE.
+If version backups should be used for FILE, and there exists
such a backup for REV or the current workfile version of file,
-return the name of it; otherwise return nil."
+return its name; otherwise return nil."
(when (vc-call make-version-backups-p file)
(let ((backup-file (vc-version-backup-file-name file rev)))
(if (file-exists-p backup-file)
(vc-resynch-buffer file t t)))
(message "Version %s has been removed from the master" target))))
-;;;autoload
+;;;###autoload
(defun vc-switch-backend (file backend)
"Make BACKEND the current version control system for FILE.
FILE must already be registered in BACKEND. The change is not
(defun vc-rename-file (old new)
"Rename file OLD to NEW, and rename its master file likewise."
(interactive "fVC rename file: \nFRename to: ")
- ;; There are several ways of renaming files under CVS 1.3, but they all
- ;; have serious disadvantages. See the FAQ (available from think.com in
- ;; pub/cvs/). I'd rather send the user an error, than do something he might
- ;; consider to be wrong. When the famous, long-awaited rename database is
- ;; implemented things might change for the better. This is unlikely to occur
- ;; until CVS 2.0 is released. --ceder 1994-01-23 21:27:51
(let ((oldbuf (get-file-buffer old))
(backend (vc-backend old)))
(unless (or (null backend) (vc-find-backend-function backend 'rename-file))
(setq default-directory (file-name-directory changelog))
(delete-file tempfile)))))
-;;; Annotate functionality
+;; Annotate functionality
;; Declare globally instead of additional parameter to
;; temp-buffer-show-function (not possible to pass more than one
;;;; the contents in BUFFER.
;;;###autoload
-(defun vc-annotate (ratio)
+(defun vc-annotate (prefix)
"Display the result of the \"Annotate\" command using colors.
\"Annotate\" is defined by `vc-BACKEND-annotate-command'. New lines
-are displayed in red, old in blue. A prefix argument specifies a
-factor for stretching the time scale.
+are displayed in red, old in blue. When given a prefix argument, asks
+for a version to annotate from, and a factor for stretching the time
+scale.
`vc-annotate-menu-elements' customizes the menu elements of the
mode-specific menu. `vc-annotate-color-map' and
`vc-annotate-very-old-color' defines the mapping of time to
colors. `vc-annotate-background' specifies the background color."
- (interactive "p")
+ (interactive "P")
(vc-ensure-vc-buffer)
- (message "Annotating...")
(let ((temp-buffer-name (concat "*Annotate " (buffer-name) "*"))
- (temp-buffer-show-function 'vc-annotate-display)
- (vc-annotate-ratio ratio)
- (vc-annotate-backend (vc-backend (buffer-file-name))))
+ (temp-buffer-show-function 'vc-annotate-display)
+ (vc-annotate-version
+ (if prefix (read-string
+ (format "Annotate from version: (default %s) "
+ (vc-workfile-version (buffer-file-name)))
+ nil nil (vc-workfile-version (buffer-file-name)))))
+ (vc-annotate-ratio
+ (if prefix (string-to-number
+ (read-string "Annotate ratio: (default 1.0) "
+ nil nil "1.0"))))
+ (vc-annotate-backend (vc-backend (buffer-file-name))))
+ (message "Annotating...")
(if (not (vc-find-backend-function vc-annotate-backend 'annotate-command))
(error "Sorry, annotating is not implemented for %s"
vc-annotate-backend))
(with-output-to-temp-buffer temp-buffer-name
(vc-call-backend vc-annotate-backend 'annotate-command
(file-name-nondirectory (buffer-file-name))
- (get-buffer temp-buffer-name)))
+ (get-buffer temp-buffer-name)
+ vc-annotate-version))
;; Don't use the temp-buffer-name until the buffer is created
;; (only after `with-output-to-temp-buffer'.)
(setq vc-annotate-buffers
tmp-cons)) ; Return the appropriate value
-;;;; (defun vc-BACKEND-annotate-difference (point) ...)
-;;;;
-;;;; Return the difference between the age of the line at point and
-;;;; the current time. Return NIL if there is no more comparison to
-;;;; be made in the buffer. Return value as defined for
-;;;; `current-time'. You can safely assume that point is placed at
-;;;; the beginning of each line, starting at `point-min'. The buffer
-;;;; that point is placed in is the Annotate output, as defined by
-;;;; the relevant backend.
-
(defun vc-annotate-display (buffer &optional color-map backend)
"Do the VC-Annotate display in BUFFER using COLOR-MAP.
The original annotating file is supposed to be handled by BACKEND.
(interactive)
(vc-call-backend (vc-backend buffer-file-name) 'check-headers))
+(defun vc-default-check-headers (backend)
+ "Default implementation of check-headers; always returns nil."
+ nil)
+
;; Back-end-dependent stuff ends here.
;; Set up key bindings for use while editing log messages
`vc-keep-workfiles' Non-nil value prevents workfiles from being
deleted when changes are checked in
- `vc-suppress-confirm' Suppresses some confirmation prompts,
- notably for reversions.
+ `vc-suppress-confirm' Suppresses some confirmation prompts.
vc-BACKEND-header Which keywords to insert when adding headers
with \\[vc-insert-headers]. Defaults to
(set-buffer-modified-p nil)
(setq buffer-file-name nil))
-;;; These things should probably be generally available
+;; These things should probably be generally available
(defun vc-file-tree-walk (dirname func &rest args)
"Walk recursively through DIRNAME.
(provide 'vc)
-;;; DEVELOPER'S NOTES ON CONCURRENCY PROBLEMS IN THIS CODE
-;;;
-;;; These may be useful to anyone who has to debug or extend the package.
-;;; (Note that this information corresponds to versions 5.x. Some of it
-;;; might have been invalidated by the additions to support branching
-;;; and RCS keyword lookup. AS, 1995/03/24)
-;;;
-;;; A fundamental problem in VC is that there are time windows between
-;;; vc-next-action's computations of the file's version-control state and
-;;; the actions that change it. This is a window open to lossage in a
-;;; multi-user environment; someone else could nip in and change the state
-;;; of the master during it.
-;;;
-;;; The performance problem is that rlog/prs calls are very expensive; we want
-;;; to avoid them as much as possible.
-;;;
-;;; ANALYSIS:
-;;;
-;;; The performance problem, it turns out, simplifies in practice to the
-;;; problem of making vc-state fast. The two other functions that call
-;;; prs/rlog will not be so commonly used that the slowdown is a problem; one
-;;; makes snapshots, the other deletes the calling user's last change in the
-;;; master.
-;;;
-;;; The race condition implies that we have to either (a) lock the master
-;;; during the entire execution of vc-next-action, or (b) detect and
-;;; recover from errors resulting from dispatch on an out-of-date state.
-;;;
-;;; Alternative (a) appears to be infeasible. The problem is that we can't
-;;; guarantee that the lock will ever be removed. Suppose a user starts a
-;;; checkin, the change message buffer pops up, and the user, having wandered
-;;; off to do something else, simply forgets about it?
-;;;
-;;; Alternative (b), on the other hand, works well with a cheap way to speed up
-;;; vc-state. Usually, if a file is registered, we can read its locked/
-;;; unlocked state and its current owner from its permissions.
-;;;
-;;; This shortcut will fail if someone has manually changed the workfile's
-;;; permissions; also if developers are munging the workfile in several
-;;; directories, with symlinks to a master (in this latter case, the
-;;; permissions shortcut will fail to detect a lock asserted from another
-;;; directory).
-;;;
-;;; Note that these cases correspond exactly to the errors which could happen
-;;; because of a competing checkin/checkout race in between two instances of
-;;; vc-next-action.
-;;;
-;;; For VC's purposes, a workfile/master pair may have the following states:
-;;;
-;;; A. Unregistered. There is a workfile, there is no master.
-;;;
-;;; B. Registered and not locked by anyone.
-;;;
-;;; C. Locked by calling user and unchanged.
-;;;
-;;; D. Locked by the calling user and changed.
-;;;
-;;; E. Locked by someone other than the calling user.
-;;;
-;;; This makes for 25 states and 20 error conditions. Here's the matrix:
-;;;
-;;; VC's idea of state
-;;; |
-;;; V Actual state RCS action SCCS action Effect
-;;; A B C D E
-;;; A . 1 2 3 4 ci -u -t- admin -fb -i<file> initial admin
-;;; B 5 . 6 7 8 co -l get -e checkout
-;;; C 9 10 . 11 12 co -u unget; get revert
-;;; D 13 14 15 . 16 ci -u -m<comment> delta -y<comment>; get checkin
-;;; E 17 18 19 20 . rcs -u -M -l unget -n ; get -g steal lock
-;;;
-;;; All commands take the master file name as a last argument (not shown).
-;;;
-;;; In the discussion below, a "self-race" is a pathological situation in
-;;; which VC operations are being attempted simultaneously by two or more
-;;; Emacsen running under the same username.
-;;;
-;;; The vc-next-action code has the following windows:
-;;;
-;;; Window P:
-;;; Between the check for existence of a master file and the call to
-;;; admin/checkin in vc-buffer-admin (apparent state A). This window may
-;;; never close if the initial-comment feature is on.
-;;;
-;;; Window Q:
-;;; Between the call to vc-workfile-unchanged-p in and the immediately
-;;; following revert (apparent state C).
-;;;
-;;; Window R:
-;;; Between the call to vc-workfile-unchanged-p in and the following
-;;; checkin (apparent state D). This window may never close.
-;;;
-;;; Window S:
-;;; Between the unlock and the immediately following checkout during a
-;;; revert operation (apparent state C). Included in window Q.
-;;;
-;;; Window T:
-;;; Between vc-state and the following checkout (apparent state B).
-;;;
-;;; Window U:
-;;; Between vc-state and the following revert (apparent state C).
-;;; Includes windows Q and S.
-;;;
-;;; Window V:
-;;; Between vc-state and the following checkin (apparent state
-;;; D). This window may never be closed if the user fails to complete the
-;;; checkin message. Includes window R.
-;;;
-;;; Window W:
-;;; Between vc-state and the following steal-lock (apparent
-;;; state E). This window may never close if the user fails to complete
-;;; the steal-lock message. Includes window X.
-;;;
-;;; Window X:
-;;; Between the unlock and the immediately following re-lock during a
-;;; steal-lock operation (apparent state E). This window may never close
-;;; if the user fails to complete the steal-lock message.
-;;;
-;;; Errors:
-;;;
-;;; Apparent state A ---
-;;;
-;;; 1. File looked unregistered but is actually registered and not locked.
-;;;
-;;; Potential cause: someone else's admin during window P, with
-;;; caller's admin happening before their checkout.
-;;;
-;;; RCS: Prior to version 5.6.4, ci fails with message
-;;; "no lock set by <user>". From 5.6.4 onwards, VC uses the new
-;;; ci -i option and the message is "<file>,v: already exists".
-;;; SCCS: admin will fail with error (ad19).
-;;;
-;;; We can let these errors be passed up to the user.
-;;;
-;;; 2. File looked unregistered but is actually locked by caller, unchanged.
-;;;
-;;; Potential cause: self-race during window P.
-;;;
-;;; RCS: Prior to version 5.6.4, reverts the file to the last saved
-;;; version and unlocks it. From 5.6.4 onwards, VC uses the new
-;;; ci -i option, failing with message "<file>,v: already exists".
-;;; SCCS: will fail with error (ad19).
-;;;
-;;; Either of these consequences is acceptable.
-;;;
-;;; 3. File looked unregistered but is actually locked by caller, changed.
-;;;
-;;; Potential cause: self-race during window P.
-;;;
-;;; RCS: Prior to version 5.6.4, VC registers the caller's workfile as
-;;; a delta with a null change comment (the -t- switch will be
-;;; ignored). From 5.6.4 onwards, VC uses the new ci -i option,
-;;; failing with message "<file>,v: already exists".
-;;; SCCS: will fail with error (ad19).
-;;;
-;;; 4. File looked unregistered but is locked by someone else.
-;;;
-;;; Potential cause: someone else's admin during window P, with
-;;; caller's admin happening *after* their checkout.
-;;;
-;;; RCS: Prior to version 5.6.4, ci fails with a
-;;; "no lock set by <user>" message. From 5.6.4 onwards,
-;;; VC uses the new ci -i option, failing with message
-;;; "<file>,v: already exists".
-;;; SCCS: will fail with error (ad19).
-;;;
-;;; We can let these errors be passed up to the user.
-;;;
-;;; Apparent state B ---
-;;;
-;;; 5. File looked registered and not locked, but is actually unregistered.
-;;;
-;;; Potential cause: master file got nuked during window P.
-;;;
-;;; RCS: will fail with "RCS/<file>: No such file or directory"
-;;; SCCS: will fail with error ut4.
-;;;
-;;; We can let these errors be passed up to the user.
-;;;
-;;; 6. File looked registered and not locked, but is actually locked by the
-;;; calling user and unchanged.
-;;;
-;;; Potential cause: self-race during window T.
-;;;
-;;; RCS: in the same directory as the previous workfile, co -l will fail
-;;; with "co error: writable foo exists; checkout aborted". In any other
-;;; directory, checkout will succeed.
-;;; SCCS: will fail with ge17.
-;;;
-;;; Either of these consequences is acceptable.
-;;;
-;;; 7. File looked registered and not locked, but is actually locked by the
-;;; calling user and changed.
-;;;
-;;; As case 6.
-;;;
-;;; 8. File looked registered and not locked, but is actually locked by another
-;;; user.
-;;;
-;;; Potential cause: someone else checks it out during window T.
-;;;
-;;; RCS: co error: revision 1.3 already locked by <user>
-;;; SCCS: fails with ge4 (in directory) or ut7 (outside it).
-;;;
-;;; We can let these errors be passed up to the user.
-;;;
-;;; Apparent state C ---
-;;;
-;;; 9. File looks locked by calling user and unchanged, but is unregistered.
-;;;
-;;; As case 5.
-;;;
-;;; 10. File looks locked by calling user and unchanged, but is actually not
-;;; locked.
-;;;
-;;; Potential cause: a self-race in window U, or by the revert's
-;;; landing during window X of some other user's steal-lock or window S
-;;; of another user's revert.
-;;;
-;;; RCS: succeeds, refreshing the file from the identical version in
-;;; the master.
-;;; SCCS: fails with error ut4 (p file nonexistent).
-;;;
-;;; Either of these consequences is acceptable.
-;;;
-;;; 11. File is locked by calling user. It looks unchanged, but is actually
-;;; changed.
-;;;
-;;; Potential cause: the file would have to be touched by a self-race
-;;; during window Q.
-;;;
-;;; The revert will succeed, removing whatever changes came with
-;;; the touch. It is theoretically possible that work could be lost.
-;;;
-;;; 12. File looks like it's locked by the calling user and unchanged, but
-;;; it's actually locked by someone else.
-;;;
-;;; Potential cause: a steal-lock in window V.
-;;;
-;;; RCS: co error: revision <rev> locked by <user>; use co -r or rcs -u
-;;; SCCS: fails with error un2
-;;;
-;;; We can pass these errors up to the user.
-;;;
-;;; Apparent state D ---
-;;;
-;;; 13. File looks like it's locked by the calling user and changed, but it's
-;;; actually unregistered.
-;;;
-;;; Potential cause: master file got nuked during window P.
-;;;
-;;; RCS: Prior to version 5.6.4, checks in the user's version as an
-;;; initial delta. From 5.6.4 onwards, VC uses the new ci -j
-;;; option, failing with message "no such file or directory".
-;;; SCCS: will fail with error ut4.
-;;;
-;;; This case is kind of nasty. Under RCS prior to version 5.6.4,
-;;; VC may fail to detect the loss of previous version information.
-;;;
-;;; 14. File looks like it's locked by the calling user and changed, but it's
-;;; actually unlocked.
-;;;
-;;; Potential cause: self-race in window V, or the checkin happening
-;;; during the window X of someone else's steal-lock or window S of
-;;; someone else's revert.
-;;;
-;;; RCS: ci will fail with "no lock set by <user>".
-;;; SCCS: delta will fail with error ut4.
-;;;
-;;; 15. File looks like it's locked by the calling user and changed, but it's
-;;; actually locked by the calling user and unchanged.
-;;;
-;;; Potential cause: another self-race --- a whole checkin/checkout
-;;; sequence by the calling user would have to land in window R.
-;;;
-;;; SCCS: checks in a redundant delta and leaves the file unlocked as usual.
-;;; RCS: reverts to the file state as of the second user's checkin, leaving
-;;; the file unlocked.
-;;;
-;;; It is theoretically possible that work could be lost under RCS.
-;;;
-;;; 16. File looks like it's locked by the calling user and changed, but it's
-;;; actually locked by a different user.
-;;;
-;;; RCS: ci error: no lock set by <user>
-;;; SCCS: unget will fail with error un2
-;;;
-;;; We can pass these errors up to the user.
-;;;
-;;; Apparent state E ---
-;;;
-;;; 17. File looks like it's locked by some other user, but it's actually
-;;; unregistered.
-;;;
-;;; As case 13.
-;;;
-;;; 18. File looks like it's locked by some other user, but it's actually
-;;; unlocked.
-;;;
-;;; Potential cause: someone released a lock during window W.
-;;;
-;;; RCS: The calling user will get the lock on the file.
-;;; SCCS: unget -n will fail with cm4.
-;;;
-;;; Either of these consequences will be OK.
-;;;
-;;; 19. File looks like it's locked by some other user, but it's actually
-;;; locked by the calling user and unchanged.
-;;;
-;;; Potential cause: the other user relinquishing a lock followed by
-;;; a self-race, both in window W.
-;;;
-;;; Under both RCS and SCCS, both unlock and lock will succeed, making
-;;; the sequence a no-op.
-;;;
-;;; 20. File looks like it's locked by some other user, but it's actually
-;;; locked by the calling user and changed.
-;;;
-;;; As case 19.
-;;;
-;;; PROBLEM CASES:
-;;;
-;;; In order of decreasing severity:
-;;;
-;;; Cases 11 and 15 are the only ones that potentially lose work.
-;;; They would require a self-race for this to happen.
-;;;
-;;; Case 13 in RCS loses information about previous deltas, retaining
-;;; only the information in the current workfile. This can only happen
-;;; if the master file gets nuked in window P.
-;;;
-;;; Case 3 in RCS and case 15 under SCCS insert a redundant delta with
-;;; no change comment in the master. This would require a self-race in
-;;; window P or R respectively.
-;;;
-;;; Cases 2, 10, 19 and 20 do extra work, but make no changes.
-;;;
-;;; Unfortunately, it appears to me that no recovery is possible in these
-;;; cases. They don't yield error messages, so there's no way to tell that
-;;; a race condition has occurred.
-;;;
-;;; All other cases don't change either the workfile or the master, and
-;;; trigger command errors which the user will see.
+;; DEVELOPER'S NOTES ON CONCURRENCY PROBLEMS IN THIS CODE
+;;
+;; These may be useful to anyone who has to debug or extend the package.
+;; (Note that this information corresponds to versions 5.x. Some of it
+;; might have been invalidated by the additions to support branching
+;; and RCS keyword lookup. AS, 1995/03/24)
+;;
+;; A fundamental problem in VC is that there are time windows between
+;; vc-next-action's computations of the file's version-control state and
+;; the actions that change it. This is a window open to lossage in a
+;; multi-user environment; someone else could nip in and change the state
+;; of the master during it.
+;;
+;; The performance problem is that rlog/prs calls are very expensive; we want
+;; to avoid them as much as possible.
+;;
+;; ANALYSIS:
+;;
+;; The performance problem, it turns out, simplifies in practice to the
+;; problem of making vc-state fast. The two other functions that call
+;; prs/rlog will not be so commonly used that the slowdown is a problem; one
+;; makes snapshots, the other deletes the calling user's last change in the
+;; master.
+;;
+;; The race condition implies that we have to either (a) lock the master
+;; during the entire execution of vc-next-action, or (b) detect and
+;; recover from errors resulting from dispatch on an out-of-date state.
+;;
+;; Alternative (a) appears to be infeasible. The problem is that we can't
+;; guarantee that the lock will ever be removed. Suppose a user starts a
+;; checkin, the change message buffer pops up, and the user, having wandered
+;; off to do something else, simply forgets about it?
+;;
+;; Alternative (b), on the other hand, works well with a cheap way to speed up
+;; vc-state. Usually, if a file is registered, we can read its locked/
+;; unlocked state and its current owner from its permissions.
+;;
+;; This shortcut will fail if someone has manually changed the workfile's
+;; permissions; also if developers are munging the workfile in several
+;; directories, with symlinks to a master (in this latter case, the
+;; permissions shortcut will fail to detect a lock asserted from another
+;; directory).
+;;
+;; Note that these cases correspond exactly to the errors which could happen
+;; because of a competing checkin/checkout race in between two instances of
+;; vc-next-action.
+;;
+;; For VC's purposes, a workfile/master pair may have the following states:
+;;
+;; A. Unregistered. There is a workfile, there is no master.
+;;
+;; B. Registered and not locked by anyone.
+;;
+;; C. Locked by calling user and unchanged.
+;;
+;; D. Locked by the calling user and changed.
+;;
+;; E. Locked by someone other than the calling user.
+;;
+;; This makes for 25 states and 20 error conditions. Here's the matrix:
+;;
+;; VC's idea of state
+;; |
+;; V Actual state RCS action SCCS action Effect
+;; A B C D E
+;; A . 1 2 3 4 ci -u -t- admin -fb -i<file> initial admin
+;; B 5 . 6 7 8 co -l get -e checkout
+;; C 9 10 . 11 12 co -u unget; get revert
+;; D 13 14 15 . 16 ci -u -m<comment> delta -y<comment>; get checkin
+;; E 17 18 19 20 . rcs -u -M -l unget -n ; get -g steal lock
+;;
+;; All commands take the master file name as a last argument (not shown).
+;;
+;; In the discussion below, a "self-race" is a pathological situation in
+;; which VC operations are being attempted simultaneously by two or more
+;; Emacsen running under the same username.
+;;
+;; The vc-next-action code has the following windows:
+;;
+;; Window P:
+;; Between the check for existence of a master file and the call to
+;; admin/checkin in vc-buffer-admin (apparent state A). This window may
+;; never close if the initial-comment feature is on.
+;;
+;; Window Q:
+;; Between the call to vc-workfile-unchanged-p in and the immediately
+;; following revert (apparent state C).
+;;
+;; Window R:
+;; Between the call to vc-workfile-unchanged-p in and the following
+;; checkin (apparent state D). This window may never close.
+;;
+;; Window S:
+;; Between the unlock and the immediately following checkout during a
+;; revert operation (apparent state C). Included in window Q.
+;;
+;; Window T:
+;; Between vc-state and the following checkout (apparent state B).
+;;
+;; Window U:
+;; Between vc-state and the following revert (apparent state C).
+;; Includes windows Q and S.
+;;
+;; Window V:
+;; Between vc-state and the following checkin (apparent state
+;; D). This window may never be closed if the user fails to complete the
+;; checkin message. Includes window R.
+;;
+;; Window W:
+;; Between vc-state and the following steal-lock (apparent
+;; state E). This window may never close if the user fails to complete
+;; the steal-lock message. Includes window X.
+;;
+;; Window X:
+;; Between the unlock and the immediately following re-lock during a
+;; steal-lock operation (apparent state E). This window may never close
+;; if the user fails to complete the steal-lock message.
+;;
+;; Errors:
+;;
+;; Apparent state A ---
+;;
+;; 1. File looked unregistered but is actually registered and not locked.
+;;
+;; Potential cause: someone else's admin during window P, with
+;; caller's admin happening before their checkout.
+;;
+;; RCS: Prior to version 5.6.4, ci fails with message
+;; "no lock set by <user>". From 5.6.4 onwards, VC uses the new
+;; ci -i option and the message is "<file>,v: already exists".
+;; SCCS: admin will fail with error (ad19).
+;;
+;; We can let these errors be passed up to the user.
+;;
+;; 2. File looked unregistered but is actually locked by caller, unchanged.
+;;
+;; Potential cause: self-race during window P.
+;;
+;; RCS: Prior to version 5.6.4, reverts the file to the last saved
+;; version and unlocks it. From 5.6.4 onwards, VC uses the new
+;; ci -i option, failing with message "<file>,v: already exists".
+;; SCCS: will fail with error (ad19).
+;;
+;; Either of these consequences is acceptable.
+;;
+;; 3. File looked unregistered but is actually locked by caller, changed.
+;;
+;; Potential cause: self-race during window P.
+;;
+;; RCS: Prior to version 5.6.4, VC registers the caller's workfile as
+;; a delta with a null change comment (the -t- switch will be
+;; ignored). From 5.6.4 onwards, VC uses the new ci -i option,
+;; failing with message "<file>,v: already exists".
+;; SCCS: will fail with error (ad19).
+;;
+;; 4. File looked unregistered but is locked by someone else.
;;;
-;;; Thus, there is no explicit recovery code.
+;; Potential cause: someone else's admin during window P, with
+;; caller's admin happening *after* their checkout.
+;;
+;; RCS: Prior to version 5.6.4, ci fails with a
+;; "no lock set by <user>" message. From 5.6.4 onwards,
+;; VC uses the new ci -i option, failing with message
+;; "<file>,v: already exists".
+;; SCCS: will fail with error (ad19).
+;;
+;; We can let these errors be passed up to the user.
+;;
+;; Apparent state B ---
+;;
+;; 5. File looked registered and not locked, but is actually unregistered.
+;;
+;; Potential cause: master file got nuked during window P.
+;;
+;; RCS: will fail with "RCS/<file>: No such file or directory"
+;; SCCS: will fail with error ut4.
+;;
+;; We can let these errors be passed up to the user.
+;;
+;; 6. File looked registered and not locked, but is actually locked by the
+;; calling user and unchanged.
+;;
+;; Potential cause: self-race during window T.
+;;
+;; RCS: in the same directory as the previous workfile, co -l will fail
+;; with "co error: writable foo exists; checkout aborted". In any other
+;; directory, checkout will succeed.
+;; SCCS: will fail with ge17.
+;;
+;; Either of these consequences is acceptable.
+;;
+;; 7. File looked registered and not locked, but is actually locked by the
+;; calling user and changed.
+;;
+;; As case 6.
+;;
+;; 8. File looked registered and not locked, but is actually locked by another
+;; user.
+;;
+;; Potential cause: someone else checks it out during window T.
+;;
+;; RCS: co error: revision 1.3 already locked by <user>
+;; SCCS: fails with ge4 (in directory) or ut7 (outside it).
+;;
+;; We can let these errors be passed up to the user.
+;;
+;; Apparent state C ---
+;;
+;; 9. File looks locked by calling user and unchanged, but is unregistered.
+;;
+;; As case 5.
+;;
+;; 10. File looks locked by calling user and unchanged, but is actually not
+;; locked.
+;;
+;; Potential cause: a self-race in window U, or by the revert's
+;; landing during window X of some other user's steal-lock or window S
+;; of another user's revert.
+;;
+;; RCS: succeeds, refreshing the file from the identical version in
+;; the master.
+;; SCCS: fails with error ut4 (p file nonexistent).
+;;
+;; Either of these consequences is acceptable.
+;;
+;; 11. File is locked by calling user. It looks unchanged, but is actually
+;; changed.
+;;
+;; Potential cause: the file would have to be touched by a self-race
+;; during window Q.
+;;
+;; The revert will succeed, removing whatever changes came with
+;; the touch. It is theoretically possible that work could be lost.
+;;
+;; 12. File looks like it's locked by the calling user and unchanged, but
+;; it's actually locked by someone else.
+;;
+;; Potential cause: a steal-lock in window V.
+;;
+;; RCS: co error: revision <rev> locked by <user>; use co -r or rcs -u
+;; SCCS: fails with error un2
+;;
+;; We can pass these errors up to the user.
+;;
+;; Apparent state D ---
+;;
+;; 13. File looks like it's locked by the calling user and changed, but it's
+;; actually unregistered.
+;;
+;; Potential cause: master file got nuked during window P.
+;;
+;; RCS: Prior to version 5.6.4, checks in the user's version as an
+;; initial delta. From 5.6.4 onwards, VC uses the new ci -j
+;; option, failing with message "no such file or directory".
+;; SCCS: will fail with error ut4.
+;;
+;; This case is kind of nasty. Under RCS prior to version 5.6.4,
+;; VC may fail to detect the loss of previous version information.
+;;
+;; 14. File looks like it's locked by the calling user and changed, but it's
+;; actually unlocked.
+;;
+;; Potential cause: self-race in window V, or the checkin happening
+;; during the window X of someone else's steal-lock or window S of
+;; someone else's revert.
+;;
+;; RCS: ci will fail with "no lock set by <user>".
+;; SCCS: delta will fail with error ut4.
+;;
+;; 15. File looks like it's locked by the calling user and changed, but it's
+;; actually locked by the calling user and unchanged.
+;;
+;; Potential cause: another self-race --- a whole checkin/checkout
+;; sequence by the calling user would have to land in window R.
+;;
+;; SCCS: checks in a redundant delta and leaves the file unlocked as usual.
+;; RCS: reverts to the file state as of the second user's checkin, leaving
+;; the file unlocked.
+;;
+;; It is theoretically possible that work could be lost under RCS.
+;;
+;; 16. File looks like it's locked by the calling user and changed, but it's
+;; actually locked by a different user.
+;;
+;; RCS: ci error: no lock set by <user>
+;; SCCS: unget will fail with error un2
+;;
+;; We can pass these errors up to the user.
+;;
+;; Apparent state E ---
+;;
+;; 17. File looks like it's locked by some other user, but it's actually
+;; unregistered.
+;;
+;; As case 13.
+;;
+;; 18. File looks like it's locked by some other user, but it's actually
+;; unlocked.
+;;
+;; Potential cause: someone released a lock during window W.
+;;
+;; RCS: The calling user will get the lock on the file.
+;; SCCS: unget -n will fail with cm4.
+;;
+;; Either of these consequences will be OK.
+;;
+;; 19. File looks like it's locked by some other user, but it's actually
+;; locked by the calling user and unchanged.
+;;
+;; Potential cause: the other user relinquishing a lock followed by
+;; a self-race, both in window W.
+;;
+;; Under both RCS and SCCS, both unlock and lock will succeed, making
+;; the sequence a no-op.
+;;
+;; 20. File looks like it's locked by some other user, but it's actually
+;; locked by the calling user and changed.
+;;
+;; As case 19.
+;;
+;; PROBLEM CASES:
+;;
+;; In order of decreasing severity:
+;;
+;; Cases 11 and 15 are the only ones that potentially lose work.
+;; They would require a self-race for this to happen.
+;;
+;; Case 13 in RCS loses information about previous deltas, retaining
+;; only the information in the current workfile. This can only happen
+;; if the master file gets nuked in window P.
+;;
+;; Case 3 in RCS and case 15 under SCCS insert a redundant delta with
+;; no change comment in the master. This would require a self-race in
+;; window P or R respectively.
+;;
+;; Cases 2, 10, 19 and 20 do extra work, but make no changes.
+;;
+;; Unfortunately, it appears to me that no recovery is possible in these
+;; cases. They don't yield error messages, so there's no way to tell that
+;; a race condition has occurred.
+;;
+;; All other cases don't change either the workfile or the master, and
+;; trigger command errors which the user will see.
+;;
+;; Thus, there is no explicit recovery code.
;;; vc.el ends here