;;; vc.el --- drive a version-control system from within Emacs
-;; Copyright (C) 1992, 93, 94, 95, 96, 97 Free Software Foundation, Inc.
+;; Copyright (C) 1992, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
;; Maintainer: Andre Spiegel <spiegel@inf.fu-berlin.de>
-;; $Id: vc.el,v 1.214 1998/03/31 18:08:36 spiegel Exp spiegel $
+;; $Id: vc.el,v 1.223 1998/04/15 10:13:07 spiegel Exp done $
;; This file is part of GNU Emacs.
;; Paul Eggert <eggert@twinsun.com>, Sebastian Kremer <sk@thp.uni-koeln.de>,
;; and Richard Stallman contributed valuable criticism, support, and testing.
;; CVS support was added by Per Cederqvist <ceder@lysator.liu.se>
-;; in Jan-Feb 1994. Further enhancements came from ttn.netcom.com and
+;; in Jan-Feb 1994. Further enhancements came from ttn@netcom.com and
;; Andre Spiegel <spiegel@inf.fu-berlin.de>.
;;
;; Supported version-control systems presently include SCCS, RCS, and CVS.
"*A string used as the default version number when a new file is registered.
This can be overriden by giving a prefix argument to \\[vc-register]."
:type 'string
- :group 'vc)
+ :group 'vc
+ :version "20.3")
(defcustom vc-command-messages nil
"*If non-nil, display run messages from back-end commands."
(defvar vc-comment-ring-index nil)
(defvar vc-last-comment-match nil)
-;; Back-portability to Emacs 18
-
-(defun file-executable-p-18 (f)
- (let ((modes (file-modes f)))
- (and modes (not (zerop (logand 292))))))
-
-(defun file-regular-p-18 (f)
- (let ((attributes (file-attributes f)))
- (and attributes (not (car attributes)))))
-
-; Conditionally rebind some things for Emacs 18 compatibility
-(if (not (boundp 'minor-mode-map-alist))
- (progn
- (setq compilation-old-error-list nil)
- (fset 'file-executable-p 'file-executable-p-18)
- (fset 'shrink-window-if-larger-than-buffer 'beginning-of-buffer)
- ))
-
-(if (not (fboundp 'file-regular-p))
- (fset 'file-regular-p 'file-regular-p-18))
-
;;; Find and compare backend releases
(defun vc-backend-release (backend)
;; return t if REV is a revision on the trunk
(not (eq nil (string-match "\\`[0-9]+\\.[0-9]+\\'" rev))))
+(defun vc-branch-p (rev)
+ ;; return t if REV is a branch revision
+ (not (eq nil (string-match "\\`[0-9]+\\(\\.[0-9]+\\.[0-9]+\\)*\\'" rev))))
+
(defun vc-branch-part (rev)
;; return the branch part of a revision number REV
(substring rev 0 (string-match "\\.[0-9]+\\'" rev)))
;; CVS
t))
-(defun vc-registration-error (file)
- (if file
- (error "File %s is not under version control" file)
- (error "Buffer %s is not associated with a file" (buffer-name))))
+(defun vc-ensure-vc-buffer ()
+ ;; Make sure that the current buffer visits a version-controlled file.
+ (if vc-dired-mode
+ (set-buffer (find-file-noselect (dired-get-filename)))
+ (while vc-parent-buffer
+ (pop-to-buffer vc-parent-buffer))
+ (if (not (buffer-file-name))
+ (error "Buffer %s is not associated with a file" (buffer-name))
+ (if (not (vc-backend (buffer-file-name)))
+ (error "File %s is not under version control" (buffer-file-name))))))
(defvar vc-binary-assoc nil)
(message "Running %s on %s..." command file))
(let ((obuf (current-buffer)) (camefrom (current-buffer))
(squeezed nil)
- (vc-file (and file (vc-name file)))
(olddir default-directory)
- status)
+ vc-file status)
(set-buffer (get-buffer-create buffer))
(set (make-local-variable 'vc-parent-buffer) camefrom)
(set (make-local-variable 'vc-parent-buffer-name)
(mapcar
(function (lambda (s) (and s (setq squeezed (append squeezed (list s))))))
flags)
- (if (and vc-file (eq last 'MASTER))
+ (if (and (eq last 'MASTER) file (setq vc-file (vc-name file)))
(setq squeezed (append squeezed (list vc-file))))
(if (and file (eq last 'WORKFILE))
(progn
(defun vc-next-action-on-file (file verbose &optional comment)
;;; If comment is specified, it will be used as an admin or checkin comment.
- (let ((vc-file (vc-name file))
- (vc-type (vc-backend file))
+ (let ((vc-type (vc-backend file))
owner version buffer)
(cond
- ;; if there is no master file corresponding, create one
- ((not vc-file)
- (vc-register verbose comment)
- (if vc-initial-comment
- (setq vc-log-after-operation-hook
- 'vc-checkout-writable-buffer-hook)
- (vc-checkout-writable-buffer file)))
+ ;; If the file is not under version control, register it
+ ((not vc-type)
+ (vc-register verbose comment))
;; CVS: changes to the master file need to be
;; merged back into the working file
(defun vc-next-action-dired (file rev comment)
;; Do a vc-next-action-on-file on all the marked files, possibly
;; passing on the log comment we've just entered.
- (let ((configuration (current-window-configuration))
- (dired-buffer (current-buffer))
+ (let ((dired-buffer (current-buffer))
(dired-dir default-directory))
(dired-map-over-marks
(let ((file (dired-get-filename)) p
(vc-next-action-on-file file nil comment)
(set-buffer dired-buffer)
(setq default-directory dired-dir)
- (vc-dired-update-line file)
- (set-window-configuration configuration)
+ (dired-do-redisplay file)
+ (set-window-configuration vc-dired-window-configuration)
(message "Processing %s...done" file))
- nil t)))
+ nil t))
+ (dired-move-to-filename))
;; Here's the major entry point.
For RCS and SCCS files:
If the file is not already registered, this registers it for version
-control and then retrieves a writable, locked copy for editing.
+control.
If the file is registered and not locked by anyone, this checks out
a writable and locked file ready for editing.
If the file is checked out and locked by the calling user, this
(catch 'nogo
(if vc-dired-mode
(let ((files (dired-get-marked-files)))
+ (set (make-local-variable 'vc-dired-window-configuration)
+ (current-window-configuration))
(if (string= ""
(mapconcat
(function (lambda (f)
(while vc-parent-buffer
(pop-to-buffer vc-parent-buffer))
(if buffer-file-name
- (vc-next-action-on-file buffer-file-name verbose)
- (vc-registration-error nil))))
+ (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
;; Remove checkin window (after the checkin so that if that fails
;; we don't zap the *VC-log* buffer and the typing therein).
(let ((logbuf (get-buffer "*VC-log*")))
- (delete-windows-on logbuf)
- (kill-buffer logbuf))
+ (cond (logbuf
+ (delete-windows-on logbuf)
+ (kill-buffer logbuf))))
;; Now make sure we see the expanded headers
(if buffer-file-name
(vc-resynch-window buffer-file-name vc-keep-workfiles t))
+ (if vc-dired-mode
+ (dired-move-to-filename))
(run-hooks after-hook 'vc-finish-logentry-hook)))
;; Code for access to the comment ring
With a prefix argument, it reads the file name to use
and two version designators specifying which versions to compare."
(interactive (list current-prefix-arg t))
- (if vc-dired-mode
- (set-buffer (find-file-noselect (dired-get-filename))))
- (while vc-parent-buffer
- (pop-to-buffer vc-parent-buffer))
+ (vc-ensure-vc-buffer)
(if historic
(call-interactively 'vc-version-diff)
- (if (or (null buffer-file-name) (null (vc-name buffer-file-name)))
- (error
- "There is no version-control master associated with this buffer"))
(let ((file buffer-file-name)
unchanged)
(vc-buffer-sync not-urgent)
If the current buffer is named `F', the version is named `F.~REV~'.
If `F.~REV~' already exists, it is used instead of being re-created."
(interactive "sVersion to visit (default is latest version): ")
- (if vc-dired-mode
- (set-buffer (find-file-noselect (dired-get-filename))))
- (while vc-parent-buffer
- (pop-to-buffer vc-parent-buffer))
- (if (and buffer-file-name (vc-name buffer-file-name))
- (let* ((version (if (string-equal rev "")
- (vc-latest-version buffer-file-name)
- rev))
- (filename (concat buffer-file-name ".~" version "~")))
- (or (file-exists-p filename)
- (vc-backend-checkout buffer-file-name nil version filename))
- (find-file-other-window filename))
- (vc-registration-error buffer-file-name)))
+ (vc-ensure-vc-buffer)
+ (let* ((version (if (string-equal rev "")
+ (vc-latest-version buffer-file-name)
+ rev))
+ (filename (concat buffer-file-name ".~" version "~")))
+ (or (file-exists-p filename)
+ (vc-backend-checkout buffer-file-name nil version filename))
+ (find-file-other-window filename)))
;; Header-insertion code
Headers desired are inserted at the start of the buffer, and are pulled from
the variable `vc-header-alist'."
(interactive)
- (if vc-dired-mode
- (find-file-other-window (dired-get-filename)))
- (while vc-parent-buffer
- (pop-to-buffer vc-parent-buffer))
+ (vc-ensure-vc-buffer)
(save-excursion
(save-restriction
(widen)
(replace-match "$\\1$")))
(vc-restore-buffer-context context)))
-(defun vc-resolve-conflicts ()
+;;;###autoload
+(defun vc-merge ()
+ (interactive)
+ (vc-ensure-vc-buffer)
+ (vc-buffer-sync)
+ (let* ((file buffer-file-name)
+ (backend (vc-backend file))
+ first-version second-version locking-user)
+ (if (eq backend 'SCCS)
+ (error "Sorry, merging is not implemented for SCCS")
+ (setq locking-user (vc-locking-user file))
+ (if (eq (vc-checkout-model file) 'manual)
+ (if (not locking-user)
+ (if (not (y-or-n-p
+ (format "File must be %s for merging. %s now? "
+ (if (eq backend 'RCS) "locked" "writable")
+ (if (eq backend 'RCS) "Lock" "Check out"))))
+ (error "Merge aborted")
+ (vc-checkout file t))
+ (if (not (string= locking-user (vc-user-login-name)))
+ (error "File is locked by %s" locking-user))))
+ (setq first-version (read-string "Branch or version to merge from: "))
+ (if (and (>= (elt first-version 0) ?0)
+ (<= (elt first-version 0) ?9))
+ (if (not (vc-branch-p first-version))
+ (setq second-version
+ (read-string "Second version: "
+ (concat (vc-branch-part first-version) ".")))
+ ;; We want to merge an entire branch. Set versions
+ ;; accordingly, so that vc-backend-merge understands us.
+ (setq second-version first-version)
+ ;; first-version must be the starting point of the branch
+ (setq first-version (vc-branch-part first-version))))
+ (let ((status (vc-backend-merge file first-version second-version)))
+ (if (and (eq (vc-checkout-model file) 'implicit)
+ (not (vc-locking-user file)))
+ (vc-file-setprop file 'vc-locking-user nil))
+ (vc-resynch-buffer file t t)
+ (if (not (zerop status))
+ (if (y-or-n-p "Conflicts detected. Resolve them now? ")
+ (vc-resolve-conflicts "WORKFILE" "MERGE SOURCE")
+ (message "File contains conflict markers"))
+ (message "Merge successful"))))))
+
+;;;###autoload
+(defun vc-resolve-conflicts (&optional name-A name-B)
"Invoke ediff to resolve conflicts in the current buffer.
The conflicts must be marked with rcsmerge conflict markers."
(interactive)
+ (vc-ensure-vc-buffer)
(let* ((found nil)
(file-name (file-name-nondirectory buffer-file-name))
(your-buffer (generate-new-buffer
- (concat "*" file-name " WORKFILE*")))
+ (concat "*" file-name
+ " " (or name-A "WORKFILE") "*")))
(other-buffer (generate-new-buffer
- (concat "*" file-name " CHECKED-IN*")))
+ (concat "*" file-name
+ " " (or name-B "CHECKED-IN") "*")))
(result-buffer (current-buffer)))
(save-excursion
(set-buffer your-buffer)
;; All VC commands get mapped into logical equivalents.
(define-derived-mode vc-dired-mode dired-mode "Dired under VC"
- "The major mode used in VC directory buffers. It is derived from Dired.
-All Dired commands operate normally. Users currently locking listed files
-are listed in place of the file's owner and group.
-Keystrokes bound to VC commands will execute as though they had been called
-on a buffer attached to the file named in the current Dired buffer line."
+ "The major mode used in VC directory buffers. It works like Dired,
+but lists only files under version control, with the current VC state of
+each file being indicated in the place of the file's link count, owner,
+group and size. Subdirectories are also listed, and you may insert them
+into the buffer as desired, like in Dired.
+ All Dired commands operate normally, with the exception of `v', which
+is redefined as the version control prefix, so that you can type
+`vl', `v=' etc. to invoke `vc-print-log', `vc-diff', and the like on
+the file named in the current Dired buffer line. `vv' invokes
+`vc-next-action' on this file, or on all files currently marked.
+There is a special command, `*l', to mark all files currently locked."
+ (make-local-hook 'dired-after-readin-hook)
+ (add-hook 'dired-after-readin-hook 'vc-dired-hook nil t)
+ ;; The following is slightly modified from dired.el,
+ ;; because file lines look a bit different in vc-dired-mode.
+ (set (make-local-variable 'dired-move-to-filename-regexp)
+ (let*
+ ((l "\\([A-Za-z]\\|[^\0-\177]\\)")
+ ;; In some locales, month abbreviations are as short as 2 letters,
+ ;; and they can be padded on the right with spaces.
+ (month (concat l l "+ *"))
+ ;; Recognize any non-ASCII character.
+ ;; The purpose is to match a Kanji character.
+ (k "[^\0-\177]")
+ ;; (k "[^\x00-\x7f\x80-\xff]")
+ (s " ")
+ (yyyy "[0-9][0-9][0-9][0-9]")
+ (mm "[ 0-1][0-9]")
+ (dd "[ 0-3][0-9]")
+ (HH:MM "[ 0-2][0-9]:[0-5][0-9]")
+ (western (concat "\\(" month s dd "\\|" dd s month "\\)"
+ s "\\(" HH:MM "\\|" s yyyy "\\)"))
+ (japanese (concat mm k s dd k s "\\(" s HH:MM "\\|" yyyy k "\\)")))
+ (concat s "\\(" western "\\|" japanese "\\)" s)))
(setq vc-dired-mode t))
(define-key vc-dired-mode-map "\C-xv" vc-prefix-map)
-(define-key vc-dired-mode-map "g" 'vc-dired-update)
+(define-key vc-dired-mode-map "v" vc-prefix-map)
(define-key vc-dired-mode-map "=" 'vc-diff)
+(defun vc-dired-mark-locked ()
+ "Mark all files currently locked."
+ (interactive)
+ (dired-mark-if (let ((f (dired-get-filename nil t)))
+ (and f
+ (not (file-directory-p f))
+ (vc-locking-user f)))
+ "locked file"))
+
+(define-key vc-dired-mode-map "*l" 'vc-dired-mark-locked)
+
+(defun vc-fetch-cvs-status (dir)
+ (let ((default-directory dir))
+ (vc-do-command "*vc-info*" 0 "cvs" nil nil "status" dir)
+ (save-excursion
+ (set-buffer (get-buffer "*vc-info*"))
+ (goto-char (point-min))
+ (while (re-search-forward "^=+\n\\([^=\n].*\n\\|\n\\)+" nil t)
+ (narrow-to-region (match-beginning 0) (match-end 0))
+ (vc-parse-cvs-status)
+ (goto-char (point-max))
+ (widen)))))
+
(defun vc-dired-state-info (file)
;; Return the string that indicates the version control status
;; on a VC dired line.
- (let ((cvs-state (and (eq (vc-backend file) 'CVS)
- (vc-cvs-status file))))
- (if cvs-state
- (cond ((eq cvs-state 'up-to-date) nil)
- ((eq cvs-state 'needs-checkout) "patch")
- ((eq cvs-state 'locally-modified) "modified")
- ((eq cvs-state 'needs-merge) "merge")
- ((eq cvs-state 'unresolved-conflict) "conflict")
- ((eq cvs-state 'locally-added) "added"))
- (vc-locking-user file))))
+ (let* ((cvs-state (and (eq (vc-backend file) 'CVS)
+ (vc-cvs-status file)))
+ (state
+ (if cvs-state
+ (cond ((eq cvs-state 'up-to-date) nil)
+ ((eq cvs-state 'needs-checkout) "patch")
+ ((eq cvs-state 'locally-modified) "modified")
+ ((eq cvs-state 'needs-merge) "merge")
+ ((eq cvs-state 'unresolved-conflict) "conflict")
+ ((eq cvs-state 'locally-added) "added"))
+ (vc-locking-user file))))
+ (if state (concat "(" state ")"))))
(defun vc-dired-reformat-line (x)
- ;; Hack a directory-listing line, plugging in locking-user info in
- ;; place of the user and group info. Should have the beneficial
- ;; side-effect of shortening the listing line. Each call starts with
- ;; point immediately following the dired mark area on the line to be
- ;; hacked.
- ;;
- ;; Simplest possible one:
- ;; (insert (concat x "\t")))
- ;;
+ ;; Reformat a directory-listing line, replacing various columns with
+ ;; version control information.
;; This code, like dired, assumes UNIX -l format.
- (let ((pos (point)) limit perm owner date-and-file)
+ (beginning-of-line)
+ (let ((pos (point)) limit perm date-and-file)
(end-of-line)
(setq limit (point))
(goto-char pos)
- (cond
- ((or
- (re-search-forward ;; owner and group
-"\\([drwxlts-]+ \\) *[0-9]+ \\([^ ]+\\) +[^ ]+ +[0-9]+\\( [^ 0-9]+ [0-9 ][0-9] .*\\)"
- limit t)
- (re-search-forward ;; only owner displayed
-"\\([drwxlts-]+ \\) *[0-9]+ \\([^ ]+\\) +[0-9]+\\( [^ 0-9]+ [0-9 ][0-9] .*\\)"
- limit t))
- (setq perm (match-string 1)
- owner (match-string 2)
- date-and-file (match-string 3)))
- ((re-search-forward ;; OS/2 -l format, no links, owner, group
-"\\([drwxlts-]+ \\) *[0-9]+\\( [^ 0-9]+ [0-9 ][0-9] .*\\)"
- limit t)
+ (when
+ (or
+ (re-search-forward ;; owner and group
+ "^\\(..[drwxlts-]+ \\) *[0-9]+ [^ ]+ +[^ ]+ +[0-9]+\\( .*\\)"
+ limit t)
+ (re-search-forward ;; only owner displayed
+ "^\\(..[drwxlts-]+ \\) *[0-9]+ [^ ]+ +[0-9]+\\( .*\\)"
+ limit t)
+ (re-search-forward ;; OS/2 -l format, no links, owner, group
+ "^\\(..[drwxlts-]+ \\) *[0-9]+\\( .*\\)"
+ limit t))
(setq perm (match-string 1)
- date-and-file (match-string 2))))
- (if x (setq x (concat "(" x ")")))
- (let ((rep (substring (concat x " ") 0 10)))
- (replace-match (concat perm rep date-and-file)))))
-
-(defun vc-dired-update-line (file)
- ;; Update the vc-dired listing line of file -- it is assumed
- ;; that point is already on this line. Don't use dired-do-redisplay
- ;; for this, because it cannot handle the way vc-dired deals with
- ;; subdirectories.
- (beginning-of-line)
- (forward-char 2)
- (let ((start (point)))
- (forward-line 1)
- (beginning-of-line)
- (delete-region start (point))
- (insert-directory file dired-listing-switches)
- (forward-line -1)
- (end-of-line)
- (delete-char (- (length file)))
- (insert (substring file (length (expand-file-name default-directory))))
- (goto-char start))
- (vc-dired-reformat-line (vc-dired-state-info file)))
-
-(defun vc-dired-update (verbose)
- (interactive "P")
- (vc-directory default-directory verbose))
+ date-and-file (match-string 2))
+ (setq x (substring (concat x " ") 0 10))
+ (replace-match (concat perm x date-and-file)))))
+
+(defun vc-dired-hook ()
+ ;; Called by dired after any portion of a vc-dired buffer has been read in.
+ ;; Reformat the listing according to version control.
+ (message "Getting version information... ")
+ (let (subdir filename (buffer-read-only nil))
+ (goto-char (point-min))
+ (while (not (eq (point) (point-max)))
+ (cond
+ ;; subdir header line
+ ((setq subdir (dired-get-subdir))
+ (if (file-directory-p (concat subdir "/CVS"))
+ (vc-fetch-cvs-status (file-name-as-directory subdir)))
+ (forward-line 1)
+ ;; erase (but don't remove) the "total" line
+ (let ((start (point)))
+ (end-of-line)
+ (delete-region start (point))
+ (beginning-of-line)
+ (forward-line 1)))
+ ;; an ordinary file line
+ ((setq filename (dired-get-filename nil t))
+ (cond
+ ((file-directory-p filename)
+ (if (member (file-name-nondirectory filename)
+ vc-directory-exclusion-list)
+ (dired-kill-line)
+ (vc-dired-reformat-line nil)
+ (forward-line 1)))
+ ((vc-backend filename)
+ (vc-dired-reformat-line (vc-dired-state-info filename))
+ (forward-line 1))
+ (t
+ (dired-kill-line))))
+ ;; any other line
+ (t (forward-line 1)))))
+ (message "Getting version information... done"))
-;;; Note in Emacs 18 the following defun gets overridden
-;;; with the symbol 'vc-directory-18. See below.
;;;###autoload
-(defun vc-directory (dirname verbose)
- "Show version-control status of the current directory and subdirectories.
-Normally it creates a Dired buffer that lists only the locked files
-in all these directories. With a prefix argument, it lists all files."
+(defun vc-directory (dirname read-switches)
(interactive "DDired under VC (directory): \nP")
- (require 'dired)
- (setq dirname (expand-file-name dirname))
- ;; force a trailing slash
- (if (not (eq (elt dirname (1- (length dirname))) ?/))
- (setq dirname (concat dirname "/")))
- (let (nonempty
- (dl (length dirname))
- (filelist nil) (statelist nil)
- (old-dir default-directory)
- dired-buf
- dired-buf-mod-count)
- (vc-file-tree-walk
- dirname
- (function
- (lambda (f)
- (if (vc-registered f)
- (let ((state (vc-dired-state-info f)))
- (and (or verbose state)
- (setq filelist (cons (substring f dl) filelist))
- (setq statelist (cons state statelist))))))))
- (save-window-excursion
- (save-excursion
- ;; This uses a semi-documented feature of dired; giving a switch
- ;; argument forces the buffer to refresh each time.
- (setq dired-buf
- (dired-internal-noselect
- (cons dirname (nreverse filelist))
- dired-listing-switches 'vc-dired-mode))
- (setq nonempty (not (eq 0 (length filelist))))))
- (switch-to-buffer dired-buf)
- ;; Make a few modifications to the header
- (setq buffer-read-only nil)
- (goto-char (point-min))
- (forward-line 1) ;; Skip header line
- (let ((start (point))) ;; Erase (but don't remove) the
- (end-of-line) ;; "wildcard" line.
- (delete-region start (point)))
- (beginning-of-line)
- (if nonempty
- (progn
- ;; Plug the version information into the individual lines
- (mapcar
- (function
- (lambda (x)
- (forward-char 2) ;; skip dired's mark area
- (vc-dired-reformat-line x)
- (forward-line 1))) ;; go to next line
- (nreverse statelist))
- (setq buffer-read-only t)
- (goto-char (point-min))
- (dired-next-line 2)
- )
- (dired-next-line 1)
- (insert " ")
- (setq buffer-read-only t)
- (message "No files are currently %s under %s"
- (if verbose "registered" "locked") dirname))
- ))
-
-;; Emacs 18 version
-(defun vc-directory-18 (verbose)
- "Show version-control status of all files under the current directory."
- (interactive "P")
- (let (nonempty (dir default-directory))
- (save-excursion
- (set-buffer (get-buffer-create "*vc-status*"))
- (erase-buffer)
- (cd dir)
- (vc-file-tree-walk
- default-directory
- (function (lambda (f)
- (if (vc-registered f)
- (let ((user (vc-locking-user f)))
- (if (or user verbose)
- (insert (format
- "%s %s\n"
- (concat user) f))))))))
- (setq nonempty (not (zerop (buffer-size)))))
-
- (if nonempty
- (progn
- (pop-to-buffer "*vc-status*" t)
- (goto-char (point-min))
- (shrink-window-if-larger-than-buffer)))
- (message "No files are currently %s under %s"
- (if verbose "registered" "locked") default-directory))
- )
-
-(or (boundp 'minor-mode-map-alist)
- (fset 'vc-directory 'vc-directory-18))
+ (let ((switches
+ (if read-switches (read-string "Dired listing switches: "
+ dired-listing-switches))))
+ (require 'dired)
+ (require 'dired-aux)
+ ;; force a trailing slash
+ (if (not (eq (elt dirname (1- (length dirname))) ?/))
+ (setq dirname (concat dirname "/")))
+ (switch-to-buffer
+ (dired-internal-noselect (expand-file-name dirname)
+ (or switches dired-listing-switches)
+ 'vc-dired-mode))))
;; Named-configuration support for SCCS
(defun vc-print-log ()
"List the change log of the current buffer in a window."
(interactive)
- (if vc-dired-mode
- (set-buffer (find-file-noselect (dired-get-filename))))
- (while vc-parent-buffer
- (pop-to-buffer vc-parent-buffer))
- (if (and buffer-file-name (vc-name buffer-file-name))
- (let ((file buffer-file-name))
- (vc-backend-print-log file)
- (pop-to-buffer (get-buffer-create "*vc*"))
- (setq default-directory (file-name-directory file))
- (goto-char (point-max)) (forward-line -1)
- (while (looking-at "=*\n")
- (delete-char (- (match-end 0) (match-beginning 0)))
- (forward-line -1))
- (goto-char (point-min))
- (if (looking-at "[\b\t\n\v\f\r ]+")
- (delete-char (- (match-end 0) (match-beginning 0))))
- (shrink-window-if-larger-than-buffer)
- ;; move point to the log entry for the current version
- (and (not (eq (vc-backend file) 'SCCS))
- (re-search-forward
- ;; also match some context, for safety
- (concat "----\nrevision " (vc-workfile-version file)
- "\\(\tlocked by:.*\n\\|\n\\)date: ") nil t)
- ;; set the display window so that
- ;; the whole log entry is displayed
- (let (start end lines)
- (beginning-of-line) (forward-line -1) (setq start (point))
- (if (not (re-search-forward "^----*\nrevision" nil t))
- (setq end (point-max))
- (beginning-of-line) (forward-line -1) (setq end (point)))
- (setq lines (count-lines start end))
- (cond
- ;; if the global information and this log entry fit
- ;; into the window, display from the beginning
- ((< (count-lines (point-min) end) (window-height))
- (goto-char (point-min))
- (recenter 0)
- (goto-char start))
- ;; if the whole entry fits into the window,
- ;; display it centered
- ((< (1+ lines) (window-height))
- (goto-char start)
- (recenter (1- (- (/ (window-height) 2) (/ lines 2)))))
- ;; otherwise (the entry is too large for the window),
- ;; display from the start
- (t
- (goto-char start)
- (recenter 0)))))
- )
- (vc-registration-error buffer-file-name)
- )
- )
+ (vc-ensure-vc-buffer)
+ (let ((file buffer-file-name))
+ (vc-backend-print-log file)
+ (pop-to-buffer (get-buffer-create "*vc*"))
+ (setq default-directory (file-name-directory file))
+ (goto-char (point-max)) (forward-line -1)
+ (while (looking-at "=*\n")
+ (delete-char (- (match-end 0) (match-beginning 0)))
+ (forward-line -1))
+ (goto-char (point-min))
+ (if (looking-at "[\b\t\n\v\f\r ]+")
+ (delete-char (- (match-end 0) (match-beginning 0))))
+ (shrink-window-if-larger-than-buffer)
+ ;; move point to the log entry for the current version
+ (and (not (eq (vc-backend file) 'SCCS))
+ (re-search-forward
+ ;; also match some context, for safety
+ (concat "----\nrevision " (vc-workfile-version file)
+ "\\(\tlocked by:.*\n\\|\n\\)date: ") nil t)
+ ;; set the display window so that
+ ;; the whole log entry is displayed
+ (let (start end lines)
+ (beginning-of-line) (forward-line -1) (setq start (point))
+ (if (not (re-search-forward "^----*\nrevision" nil t))
+ (setq end (point-max))
+ (beginning-of-line) (forward-line -1) (setq end (point)))
+ (setq lines (count-lines start end))
+ (cond
+ ;; if the global information and this log entry fit
+ ;; into the window, display from the beginning
+ ((< (count-lines (point-min) end) (window-height))
+ (goto-char (point-min))
+ (recenter 0)
+ (goto-char start))
+ ;; if the whole entry fits into the window,
+ ;; display it centered
+ ((< (1+ lines) (window-height))
+ (goto-char start)
+ (recenter (1- (- (/ (window-height) 2) (/ lines 2)))))
+ ;; otherwise (the entry is too large for the window),
+ ;; display from the start
+ (t
+ (goto-char start)
+ (recenter 0)))))))
;;;###autoload
(defun vc-revert-buffer ()
automatically pick up newer changes found in the master file;
use C-u \\[vc-next-action] RET to do so."
(interactive)
- (if vc-dired-mode
- (find-file-other-window (dired-get-filename)))
- (while vc-parent-buffer
- (pop-to-buffer vc-parent-buffer))
+ (vc-ensure-vc-buffer)
(let ((file buffer-file-name)
;; This operation should always ask for confirmation.
(vc-suppress-confirm nil)
"Get rid of most recently checked in version of this file.
A prefix argument means do not revert the buffer afterwards."
(interactive "P")
- (if vc-dired-mode
- (find-file-other-window (dired-get-filename)))
- (while vc-parent-buffer
- (pop-to-buffer vc-parent-buffer))
+ (vc-ensure-vc-buffer)
(cond
- ((not (vc-registered (buffer-file-name)))
- (vc-registration-error (buffer-file-name)))
((eq (vc-backend (buffer-file-name)) 'CVS)
(error "Unchecking files under CVS is dangerous and not supported in VC"))
((vc-locking-user (buffer-file-name))
`vc-annotate-very-old-color' defines the mapping of time to
colors. `vc-annotate-background' specifies the background color."
(interactive "p")
- (if (not (eq (vc-buffer-backend) 'CVS)) ; This only works with CVS
- (vc-registration-error (buffer-file-name)))
+ (vc-ensure-vc-buffer)
+ (if (not (eq (vc-backend (buffer-file-name)) 'CVS))
+ (error "Sorry, vc-annotate is only implemented for CVS"))
(message "Annotating...")
(let ((temp-buffer-name (concat "*cvs annotate " (buffer-name) "*"))
(temp-buffer-show-function 'vc-annotate-display)
;; Checking out explicit versions is not supported under SCCS, yet.
;; We always "revert" to the latest version; therefore
;; vc-workfile-version is cleared here so that it gets recomputed.
- (vc-file-setprop 'vc-workfile-version nil))
+ (vc-file-setprop file 'vc-workfile-version nil))
;; RCS
(vc-do-command nil 0 "co" file 'MASTER
"-f" (concat "-u" (vc-workfile-version file)))
(and newvers (concat "-r" newvers))
(if (listp diff-switches)
diff-switches
- (list diff-switches)))))
- (t
- (vc-registration-error file)))))
+ (list diff-switches))))))))
(defun vc-backend-merge-news (file)
;; Merge in any new changes made to FILE.
(error "Couldn't analyze cvs update result"))))
(message "Merging changes into %s...done" file)))
+(defun vc-backend-merge (file first-version &optional second-version)
+ ;; Merge the changes between FIRST-VERSION and SECOND-VERSION into
+ ;; the current working copy of FILE. It is assumed that FILE is
+ ;; locked and writable (vc-merge ensures this).
+ (vc-backend-dispatch file
+ ;; SCCS
+ (error "Sorry, merging is not implemented for SCCS")
+ ;; RCS
+ (vc-do-command nil 1 "rcsmerge" file 'MASTER
+ "-kk" ;; ignore keyword conflicts
+ (concat "-r" first-version)
+ (if second-version (concat "-r" second-version)))
+ ;; CVS
+ (progn
+ (vc-do-command nil 0 "cvs" file 'WORKFILE
+ "update" "-kk"
+ (concat "-j" first-version)
+ (concat "-j" second-version))
+ (save-excursion
+ (set-buffer (get-buffer "*vc*"))
+ (goto-char (point-min))
+ (if (re-search-forward "conflicts during merge" nil t)
+ 1 ;; signal error
+ 0 ;; signal success
+ )))))
+
(defun vc-check-headers ()
"Check if the current file has any headers in it."
(interactive)