Mercurial > emacs
changeset 2490:70d00ecacc0d
(vc-directory. vc-start-entry, vc-next-action, vc-next-action-on-file):
The vc-directory listing is now in an augmented Dired mode that supports
vc-next-action on all marked files.
author | Eric S. Raymond <esr@snark.thyrsus.com> |
---|---|
date | Thu, 08 Apr 1993 16:35:52 +0000 |
parents | b626f5b9a0df |
children | 5f3061858f47 |
files | lisp/vc.el |
diffstat | 1 files changed, 235 insertions(+), 147 deletions(-) [+] |
line wrap: on
line diff
--- a/lisp/vc.el Thu Apr 08 16:35:48 1993 +0000 +++ b/lisp/vc.el Thu Apr 08 16:35:52 1993 +0000 @@ -3,9 +3,7 @@ ;; Copyright (C) 1992 Free Software Foundation, Inc. ;; Author: Eric S. Raymond <esr@snark.thyrsus.com> -;; Version: 5.3 - -;; $Id: vc.el,v 1.30 1993/03/29 15:38:31 eric Exp roland $ +;; Version: 5.4 ;; This file is part of GNU Emacs. @@ -45,7 +43,8 @@ ;; function vc-comment-to-change-log should prove a useful checkin hook. ;; ;; This code depends on call-process passing back the subprocess exit -;; status. Thus, you need Emacs 18.58 or later to run it. +;; status. Thus, you need Emacs 18.58 or later to run it. For the +;; vc-directory command to work properly, you need 19 ;; ;; The vc code maintains some internal state in order to reduce expensive ;; version-control operations to a minimum. Some names are only computed @@ -59,13 +58,14 @@ ;;; Code: (require 'vc-hooks) +(require 'dired) ;; General customization (defvar vc-default-back-end nil "*Back-end actually used by this interface; may be SCCS or RCS. The value is only computed when needed to avoid an expensive search.") -(defvar vc-diff-options '("-a" "-c1") +(defvar vc-diff-options '("-a" "-c2") "*The command/flags list to be used in constructing diff commands.") (defvar vc-suppress-confirm nil "*If non-nil, reat user as expert; suppress yes-no prompts on some things.") @@ -116,6 +116,8 @@ (defconst vc-name-assoc-file "VC-names") +(make-variable-buffer-local 'vc-dired-mode) + ;; File property caching (defun vc-file-clearprops (file) @@ -231,18 +233,45 @@ ;; Revert buffer, try to keep point and mark where user expects them in spite ;; of changes because of expanded version-control key words. ;; This is quite important since otherwise typeahead won't work as expected. + ;; The algorithm for reparsing the *compilation* buffer if necessary was + ;; contributed by Johnathan Vail and Kevin Rodgers. (interactive "P") (widen) (let ((point-context (vc-position-context (point))) ;; Use mark-marker to avoid confusion in transient-mark-mode. (mark-context (if (eq (marker-buffer (mark-marker)) (current-buffer)) (vc-position-context (mark-marker)))) + ;; We may want to reparse the compilation buffer after revert + (reparse (and (boundp 'compilation-error-list) + (listp compilation-error-list) + (let ((buffer (current-buffer)) + (errors compilation-error-list) + (buffer-error-marked-p nil)) + (while (and errors (not buffer-error-marked-p)) + (if (eq (marker-buffer + (car (cdr (car errors)))) + buffer) + (setq buffer-error-marked-p t)) + (setq errors (cdr errors))) + buffer-error-marked-p))) ;; Make the right thing happen in transient-mark-mode. (mark-active nil)) ;; the actual revisit (revert-buffer arg no-confirm) + ;; Reparse remaining *compilation* errors, if necessary: + (if reparse ; see next-error (compile.el) + (save-excursion + (set-buffer "*compilation*") + (set-buffer-modified-p nil) ; ? + (if (consp compilation-error-list) ; not t, nor () + (setq compilation-parsing-end + (marker-position + (car (car compilation-error-list))))) + (compilation-forget-errors) + (compilation-parse-errors))) + ;; Restore point and mark (let ((new-point (vc-find-position-by-context point-context))) (if new-point (goto-char new-point))) @@ -276,6 +305,68 @@ )) ))) +(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 (owner version (vc-file (vc-name file))) + (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-writeable-buffer-hook) + (vc-checkout-writeable-buffer))) + + ;; if there is no lock on the file, assert one and get it + ((not (setq owner (vc-locking-user file))) + (vc-checkout-writeable-buffer)) + + ;; a checked-out version exists, but the user may not own the lock + ((not (string-equal owner (user-login-name))) + (if comment + (error "Sorry, you can't steal the lock on %s this way." file)) + (vc-steal-lock + file + (and verbose (read-string "Version to steal: ")) + owner)) + + ;; OK, user owns the lock on the file + (t (let (file-window) + (find-file file) + + ;; give luser a chance to save before checking in. + (vc-buffer-sync) + + ;; Revert if file is unchanged and buffer is too. + ;; If buffer is modified, that means the user just said no + ;; to saving it; in that case, don't revert, + ;; because the user might intend to save + ;; after finishing the log entry. + (if (and (vc-workfile-unchanged-p file) + (not (buffer-modified-p))) + (progn + (vc-backend-revert file) + ;; DO NOT revert the file without asking the user! + (vc-resynch-window file t nil)) + + ;; user may want to set nonstandard parameters + (if verbose + (setq version (read-string "New version level: "))) + + ;; OK, let's do the checkin + (vc-checkin file version comment) + )))))) + +(defun vc-next-action-dired (file rev comment) + ;; We've accepted a log comment, now do a vc-next-action using it on all + ;; marked files. + (set-buffer vc-parent-buffer) + (dired-map-over-marks + (save-window-excursion + (vc-next-action-on-file (dired-get-filename) nil comment)) nil t) + ) + ;; Here's the major entry point. ;;;###autoload @@ -288,70 +379,33 @@ If the file is checked out and locked by the calling user, this first checks to see if the file has changed since checkout. If not, it performs a revert. - If the file has been changed, this pops up a buffer for creation of -a log message; when the message has been entered, it checks in the + If the file has been changed, this pops up a buffer for entry +of a log message; when the message has been entered, it checks in the resulting changes along with the log message as change commentary. If the variable vc-keep-workfiles is non-nil (which is its default), a read-only copy of the changed file is left in place afterwards. If the file is registered and locked by someone else, you are given -the option to steal the lock." +the option to steal the lock. + If you call this from within a VC dired buffer with no files marked, +it will operate on the file in the current line. + If you call this from within a VC dired buffer, and one or more +files are marked, it will accept a log message and then operate on +each one. The log message will be used as a comment for any register +or checkin operations, but ignored when doing checkouts. Attempted +lock steals will raise an error." (interactive "P") - (while vc-parent-buffer + (if vc-dired-mode + (let ((files (dired-get-marked-files))) + (if (null files) + (find-file-other-window (dired-get-filename)) + (vc-start-entry nil nil nil + "Enter a change comment." + 'vc-next-action-dired))) + (while vc-parent-buffer (pop-to-buffer vc-parent-buffer)) - (if buffer-file-name - (let - (do-update owner version - (file buffer-file-name) - (vc-file (vc-name buffer-file-name)) - (err-msg nil) - owner) - - (cond - - ;; if there is no master file corresponding, create one - ((not vc-file) - (vc-register verbose) - (if vc-initial-comment - (setq vc-log-after-operation-hook - 'vc-checkout-writeable-buffer-hook) - (vc-checkout-writeable-buffer))) - - ;; if there is no lock on the file, assert one and get it - ((not (setq owner (vc-locking-user file))) - (vc-checkout-writeable-buffer)) - - ;; a checked-out version exists, but the user may not own the lock - ((not (string-equal owner (user-login-name))) - (vc-steal-lock - file - (and verbose (read-string "Version to steal: ")) - owner)) - - ;; OK, user owns the lock on the file - (t (progn - - ;; give luser a chance to save before checking in. - (vc-buffer-sync) - - ;; Revert if file is unchanged and buffer is too. - ;; If buffer is modified, that means the user just said no - ;; to saving it; in that case, don't revert, - ;; because the user might intend to save - ;; after finishing the log entry. - (if (and (vc-workfile-unchanged-p file) - (not (buffer-modified-p))) - (progn - (vc-backend-revert file) - ;; DO NOT revert the file without asking the user! - (vc-resynch-window file t nil)) - - ;; user may want to set nonstandard parameters - (if verbose - (setq version (read-string "New version level: "))) - - ;; OK, let's do the checkin - (vc-checkin file version)))))) - (error "There is no file associated with buffer %s" (buffer-name)))) + (if buffer-file-name + (vc-next-action-on-file buffer-file-name verbose) + (error "There is no file associated with buffer %s" (buffer-name))))) ;;; These functions help the vc-next-action entry point @@ -361,7 +415,7 @@ ) ;;;###autoload -(defun vc-register (&optional override) +(defun vc-register (&optional override comment) "Register the current file into your version-control system." (interactive "P") (if (vc-name buffer-file-name) @@ -375,7 +429,9 @@ (vc-buffer-sync) (vc-admin buffer-file-name - (and override (read-string "Initial version level: "))) + (and override + (read-string + (format "Initial version level for %s: " buffer-file-name)))) ) (defun vc-resynch-window (file &optional keep noquery) @@ -394,27 +450,48 @@ (delete-window) (kill-buffer (current-buffer)))))) +(defun vc-start-entry (file rev comment msg action) + ;; Accept a comment for an operation on FILE revision REV. If COMMENT + ;; is nil, pop up a VC-log buffer, emit MSG, and set the + ;; action on close to ACTION; otherwise, do action immediately. + ;; Remember the file's buffer in parent-buffer (current one if no file). + (let ((parent (if file (find-file-noselect file) (current-buffer)))) + (if comment + (set-buffer (get-buffer-create "*VC-log*")) + (pop-to-buffer (get-buffer-create "*VC-log*"))) + (make-local-variable 'vc-parent-buffer) + (setq vc-parent-buffer parent) + (vc-mode-line (if file (file-name-nondirectory file) " (no file)")) + (vc-log-mode) + (setq vc-log-operation action) + (setq vc-log-file file) + (setq vc-log-version rev) + (if comment + (progn + (erase-buffer) + (if (not (eq comment t)) + (insert comment)) + (vc-finish-logentry)) + (message "%s Type C-c C-c when done." msg)))) -(defun vc-admin (file rev) +(defun vc-admin (file rev &optional comment) "Check a file into your version-control system. FILE is the unmodified name of the file. REV should be the base version -level to check it in under." - (if vc-initial-comment - (let ((camefrom (current-buffer))) - (pop-to-buffer (get-buffer-create "*VC-log*")) - (make-local-variable 'vc-parent-buffer) - (setq vc-parent-buffer camefrom) - (vc-log-mode) - (narrow-to-region (point-max) (point-max)) - (vc-mode-line file (file-name-nondirectory file)) - (setq vc-log-operation 'vc-backend-admin) - (setq vc-log-file file) - (setq vc-log-version rev) - (message "Enter initial comment. Type C-c C-c when done.")) - (progn - (vc-backend-admin file rev) - ;; Inhibit query here, since otherwise we always get asked. - (vc-resynch-window file vc-keep-workfiles t)))) +level to check it in under. COMMENT, if specified, is the checkin comment." + (vc-start-entry file rev + (or comment (not vc-initial-comment)) + "Enter initial comment." 'vc-backend-admin)) + +(defun vc-checkout (file &optional writeable) + "Retrieve a copy of the latest version of the given file." + ;; If ftp is on this system and the name matches the ange-ftp format + ;; for a remote file, the user is trying something that won't work. + (if (and (string-match "^/[^/:]+:" file) (vc-find-binary "ftp")) + (error "Sorry, you can't check out files over FTP")) + (vc-backend-checkout file writeable) + (if (string-equal file buffer-file-name) + (vc-resynch-window file t t)) + ) (defun vc-steal-lock (file rev &optional owner) "Steal the lock on the current workfile." @@ -443,17 +520,6 @@ (vc-backend-steal file version) (vc-resynch-window file t t)) -(defun vc-checkout (file &optional writeable) - "Retrieve a copy of the latest version of the given file." - ;; If ftp is on this system and the name matches the ange-ftp format - ;; for a remote file, the user is trying something that won't work. - (if (and (string-match "^/[^/:]+:" file) (vc-find-binary "ftp")) - (error "Sorry, you can't check out files over FTP")) - (vc-backend-checkout file writeable) - (if (string-equal file buffer-file-name) - (vc-resynch-window file t t)) - ) - (defun vc-checkin (file &optional rev comment) "Check in the file specified by FILE. The optional argument REV may be a string specifying the new version level @@ -461,32 +527,13 @@ permissions zeroed, or deleted (according to the value of vc-keep-workfiles). COMMENT is a comment string; if omitted, a buffer is popped up to accept a comment." - (let ((camefrom (current-buffer))) - (pop-to-buffer (get-buffer-create "*VC-log*")) - (make-local-variable 'vc-parent-buffer) - (setq vc-parent-buffer camefrom)) - (vc-log-mode) - (narrow-to-region (point-max) (point-max)) - (vc-mode-line file (file-name-nondirectory file)) - (setq vc-log-operation 'vc-backend-checkin - vc-log-file file - vc-log-version rev - vc-log-after-operation-hook 'vc-checkin-hook) - (message "Enter log message. Type C-c C-c when done.") - (if comment - (progn - (insert comment) - (vc-finish-logentry)))) + (setq vc-log-after-operation-hook 'vc-checkin-hook) + (vc-start-entry file rev comment "Enter a change comment." 'vc-backend-checkin)) ;;; Here is a checkin hook that may prove useful to sites using the ;;; ChangeLog facility supported by Emacs. -(defun vc-comment-to-change-log (&optional file) - "\ -Update change log from comments entered into VC for the currently visited file. -Optional arg specifies the change log file name; see `find-change-log'. -See `vc-update-change-log'." - (interactive) - (let ((log (find-change-log file))) +(defun vc-comment-to-change-log () + (let ((log (find-change-log))) (if log (let ((default-directory (or (file-name-directory log) default-directory))) @@ -510,24 +557,25 @@ (forward-char -1)) (exchange-point-and-mark) ;; Check for errors - (vc-backend-logentry-check vc-log-file) - ) + (vc-backend-logentry-check vc-log-file)) ;; OK, do it to it (if vc-log-operation - (funcall vc-log-operation - vc-log-file - vc-log-version - (buffer-string)) + (save-excursion + (funcall vc-log-operation + vc-log-file + vc-log-version + (buffer-string))) (error "No log operation is pending.")) ;; Return to "parent" buffer of this checkin and remove checkin window - (pop-to-buffer (get-file-buffer vc-log-file)) - (delete-window (get-buffer-window "*VC-log*")) - (bury-buffer "*VC-log*") + (pop-to-buffer vc-parent-buffer) + (vc-error-occurred + (delete-window (get-buffer-window "*VC-log*"))) + (kill-buffer "*VC-log*") (bury-buffer "*VC-comment-ring*") ;; Now make sure we see the expanded headers - (vc-resynch-window buffer-file-name vc-keep-workfiles t) - (run-hooks vc-log-after-operation-hook) - ) + (if buffer-file-name + (vc-resynch-window buffer-file-name vc-keep-workfiles t)) + (run-hooks vc-log-after-operation-hook)) ;; Code for access to the comment ring @@ -589,6 +637,8 @@ (defun vc-diff (historic) "Display diffs between file versions." (interactive "P") + (if vc-dired-mode + (set-buffer (find-file-noselect (dired-get-filename)))) (while vc-parent-buffer (pop-to-buffer vc-parent-buffer)) (if historic @@ -628,6 +678,7 @@ (or rel2 "current workfile(s)") ":\n\n") (set-buffer (get-buffer-create "*vc*")) + (cd file) (vc-file-tree-walk (function (lambda (f) (message "Looking at %s" f) @@ -662,6 +713,8 @@ 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)) (save-excursion @@ -686,30 +739,57 @@ ) ))))) -;; Status-checking functions +;; The VC directory submode. Coopt Dired for this. +;; All VC commands get mapped into logical equivalents. + +(or (assq 'vc-dired-mode minor-mode-map-alist) + (setq minor-mode-map-alist + (cons 'vc-dired-mode minor-mode-map-alist))) + +(defun vc-dired-mode () + "The augmented Dired minor mode used in VC directory buffers. +All Dired commands operate normally. Users currently locking listed files +are listed at the left-hand side of the buffer, following the Dired mark area. +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." + (setq vc-dired-mode t) + (setq vc-mode " under VC")) ;;;###autoload (defun vc-directory (verbose) "Show version-control status of all files under the current directory." (interactive "P") - (let (nonempty) + (let (nonempty + (dl (length default-directory)) + (filelist nil) (userlist nil) + dired-buf) + (vc-file-tree-walk + (function (lambda (f) + (if (vc-registered f) + (let ((user (vc-locking-user f))) + (and (or verbose user) + (setq filelist (cons (substring f dl) filelist)) + (setq userlist (cons user userlist)))))))) (save-excursion - (set-buffer (get-buffer-create "*vc-status*")) - (erase-buffer) - (vc-file-tree-walk - (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))))) + (dired (cons default-directory (nreverse filelist))) + (setq dired-buf (current-buffer)) + (setq nonempty (not (zerop (buffer-size))))) (if nonempty (progn - (pop-to-buffer "*vc-status*" t) - (vc-shrink-to-fit) - (goto-char (point-min))) + (pop-to-buffer dired-buf) + (vc-dired-mode) + (goto-char (point-min)) + (setq buffer-read-only nil) + (mapcar + (function (lambda (x) + (forward-char 2) ;; skip dired's mark area + (if x (insert x)) + (insert "\t") + (forward-line 1))) + (cons "\t" (nreverse userlist))) + (setq buffer-read-only t) + (goto-char (point-min)) + ) (message "No files are currently %s under %s" (if verbose "registered" "locked") default-directory)) )) @@ -794,6 +874,8 @@ (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)) @@ -813,6 +895,8 @@ This asks for confirmation if the buffer contents are not identical to that version." (interactive) + (if vc-dired-mode + (find-file-other-window (dired-get-filename))) (while vc-parent-buffer (pop-to-buffer vc-parent-buffer)) (let ((file buffer-file-name) @@ -834,6 +918,8 @@ (defun vc-cancel-version (norevert) "Undo your latest checkin." (interactive "P") + (if vc-dired-mode + (find-file-other-window (dired-get-filename))) (while vc-parent-buffer (pop-to-buffer vc-parent-buffer)) (let* ((target (concat (vc-latest-version (buffer-file-name)))) @@ -909,7 +995,7 @@ (goto-char (point-min)) (push-mark) (message "Computing change log entries...") - (message "Computing change log entries...%s" + (message "Computing change log entries... %s" (if (eq 0 (apply 'call-process "rcs2log" nil t nil args)) "done" "failed"))) @@ -994,6 +1080,7 @@ (defun vc-locking-user (file) "Return the name of the person currently holding a lock on FILE. Return nil if there is no such person." + (setq file (expand-file-name file)) ;; ??? Work around bug in 19.0.4 (if (or (not vc-keep-workfiles) (eq vc-mistrust-permissions 't) (and vc-mistrust-permissions @@ -1007,7 +1094,8 @@ ;; hack is that calls to the very expensive vc-fetch-properties ;; function only have to be made if (a) the file is locked by someone ;; other than the current user, or (b) some untoward manipulation - ;; behind vc's back has twiddled the `group' or `other' write bits. + ;; behind vc's back has changed the owner or the `group' or `other' + ;; write bits. (let ((attributes (file-attributes file))) (cond ((string-match ".r-.r-.r-." (nth 8 attributes)) nil)