]> code.delx.au - gnu-emacs/blobdiff - lisp/pcvs.el
(Fformat): Add comment about the treatment of 0 as a multibyte
[gnu-emacs] / lisp / pcvs.el
index ffec60e673ba859b0660d8bddd909f9edb750268..32631c09da92c87c9423104f213977882ab24b04 100644 (file)
@@ -14,7 +14,7 @@
 ;;     (Jari Aalto+mail.emacs) jari.aalto@poboxes.com
 ;; Maintainer: (Stefan Monnier) monnier+lists/cvs/pcl@flint.cs.yale.edu
 ;; Keywords: CVS, version control, release management
-;; Revision: $Id: pcvs.el,v 1.36 2002/06/18 21:50:30 monnier Exp $
+;; Revision: $Id: pcvs.el,v 1.45 2002/11/18 20:53:24 rost Exp $
 
 ;; This file is part of GNU Emacs.
 
@@ -62,7 +62,6 @@
 ;; ******** FIX THE DOCUMENTATION *********
 ;; 
 ;; - rework the displaying of error messages.
-;; - use UP-TO-DATE rather than DEAD when cleaning before `examine'.
 ;; - allow to flush messages only
 ;; - allow to protect files like ChangeLog from flushing
 ;; - automatically cvs-mode-insert files from find-file-hook
@@ -339,7 +338,8 @@ the primay since reading the primary can deactivate it."
   "This mode is used for buffers related to a main *cvs* buffer.
 All the `cvs-mode' buffer operations are simply rebound under
 the \\[cvs-mode-map] prefix."
-  nil " CVS")
+  nil " CVS"
+  :group 'pcl-cvs)
 (put 'cvs-minor-mode 'permanent-local t)
 
 
@@ -538,7 +538,13 @@ Working dir: " (abbreviate-file-name dir) "
                             (if cvs-cvsroot (list "-d" cvs-cvsroot))
                             args
                             files))
-              (process-connection-type nil) ; Use a pipe, not a pty.
+              ;; If process-connection-type is nil and the repository
+              ;; is accessed via SSH, a bad interaction between libc,
+              ;; CVS and SSH can lead to garbled output.
+              ;; It might be a glibc-specific problem.
+              ;; Until the problem is cleared, we'll use a pty rather than
+              ;; a pipe.
+              ;; (process-connection-type nil) ; Use a pipe, not a pty.
               (process
                ;; the process will be run in the selected dir
                (let ((default-directory (cvs-expand-dir-name dir)))
@@ -559,15 +565,23 @@ Working dir: " (abbreviate-file-name dir) "
 
 (defun cvs-update-header (args fis) ; inline
   (let* ((lastarg nil)
-        ;; filter out the largish commit message
         (args (mapcar (lambda (arg)
                         (cond
+                         ;; filter out the largish commit message
                          ((and (eq lastarg nil) (string= arg "commit"))
                           (setq lastarg 'commit) arg)
                          ((and (eq lastarg 'commit) (string= arg "-m"))
                           (setq lastarg '-m) arg)
                          ((eq lastarg '-m)
                           (setq lastarg 'done) "<log message>")
+                         ;; filter out the largish `admin -mrev:msg' message
+                         ((and (eq lastarg nil) (string= arg "admin"))
+                          (setq lastarg 'admin) arg)
+                         ((and (eq lastarg 'admin)
+                               (string-match "\\`-m[^:]*:" arg))
+                          (setq lastarg 'done)
+                          (concat (match-string 0 arg) "<log message>"))
+                         ;; Keep the rest as is.
                          (t arg)))
                       args))
         ;; turn them into a string
@@ -627,10 +641,13 @@ it is finished."
          (save-excursion (eval cvs-postproc))
          ;; check whether something is left
          (unless cvs-postprocess
+           ;; IIRC, we enable undo again once the process is finished
+           ;; for cases where the output was inserted in *vc-diff* or
+           ;; in a file-like buffer.  -stef
            (buffer-enable-undo)
            (with-current-buffer cvs-buffer
              (cvs-update-header nil nil) ;FIXME: might need to be inline
-             (message "CVS process has completed"))))
+             (message "CVS process has completed in %s" (buffer-name)))))
        ;; This might not even be necessary
        (set-buffer obuf)))))
 
@@ -640,45 +657,41 @@ DCD is the `dont-change-disc' flag to use when parsing that output.
 SUBDIR is the subdirectory (if any) where this command was run.
 OLD-FIS is the list of fileinfos on which the cvs command was applied and
   which should be considered up-to-date if they are missing from the output."
-  (let* ((from-buf (current-buffer))
-        (fileinfos (cvs-parse-buffer 'cvs-parse-table dcd subdir))
-        (_ (set-buffer cvs-buffer))
-        last
-        (from-pt (point)))
-    ;; Expand OLD-FIS to actual files.
-    (dolist (fi old-fis)
-      (when (eq (cvs-fileinfo->type fi) 'DIRCHANGE)
-       (setq old-fis (nconc (ewoc-collect cvs-cookies 'cvs-dir-member-p
-                                          (cvs-fileinfo->dir fi))
-                            old-fis))))
-    ;; Drop OLD-FIS which were already up-to-date.
-    (let ((fis nil))
+  (let* ((fileinfos (cvs-parse-buffer 'cvs-parse-table dcd subdir))
+        last)
+    (with-current-buffer cvs-buffer
+      ;; Expand OLD-FIS to actual files.
+      (let ((fis nil))
+       (dolist (fi old-fis)
+         (setq fis (if (eq (cvs-fileinfo->type fi) 'DIRCHANGE)
+                       (nconc (ewoc-collect cvs-cookies 'cvs-dir-member-p
+                                            (cvs-fileinfo->dir fi))
+                              fis)
+                     (cons fi fis))))
+       (setq old-fis fis))
+      ;; Drop OLD-FIS which were already up-to-date.
+      (let ((fis nil))
+       (dolist (fi old-fis)
+         (unless (eq (cvs-fileinfo->type fi) 'UP-TO-DATE) (push fi fis)))
+       (setq old-fis fis))
+      ;; Add the new fileinfos to the ewoc.
+      (dolist (fi fileinfos)
+       (setq last (cvs-addto-collection cvs-cookies fi last))
+       ;; This FI was in the output, so remove it from OLD-FIS.
+       (setq old-fis (delq (ewoc-data last) old-fis)))
+      ;; Process the "silent output" (i.e. absence means up-to-date).
       (dolist (fi old-fis)
-       (unless (eq (cvs-fileinfo->type fi) 'UP-TO-DATE) (push fi fis)))
-      (setq old-fis fis))
-    ;; Add the new fileinfos to the ewoc.
-    (dolist (fi fileinfos)
-      (setq last (cvs-addto-collection cvs-cookies fi last))
-      ;; This FI was in the output, so remove it from OLD-FIS.
-      (setq old-fis (delq (ewoc-data last) old-fis)))
-    ;; Process the "silent output" (i.e. absence means up-to-date).
-    (dolist (fi old-fis)
-      (setf (cvs-fileinfo->type fi) 'UP-TO-DATE)
-      (setq last (cvs-addto-collection cvs-cookies fi last)))
-    (setq fileinfos (nconc old-fis fileinfos))
-    ;; Clean up the ewoc as requested by the user.
-    (cvs-cleanup-collection cvs-cookies
-                           (eq cvs-auto-remove-handled t)
-                           cvs-auto-remove-directories
-                           nil)
-    ;; Revert buffers if necessary.
-    (when (and cvs-auto-revert (not dcd) (not cvs-from-vc))
-      (cvs-revert-if-needed fileinfos))
-    ;; get back to where we were.  `save-excursion' doesn't seem to
-    ;; work in this case, probably because the buffer is reconstructed
-    ;; by the cookie code.
-    (goto-char from-pt)
-    (set-buffer from-buf)))
+       (setf (cvs-fileinfo->type fi) 'UP-TO-DATE)
+       (setq last (cvs-addto-collection cvs-cookies fi last)))
+      (setq fileinfos (nconc old-fis fileinfos))
+      ;; Clean up the ewoc as requested by the user.
+      (cvs-cleanup-collection cvs-cookies
+                             (eq cvs-auto-remove-handled t)
+                             cvs-auto-remove-directories
+                             nil)
+      ;; Revert buffers if necessary.
+      (when (and cvs-auto-revert (not dcd) (not cvs-from-vc))
+       (cvs-revert-if-needed fileinfos)))))
 
 (defmacro defun-cvs-mode (fun args docstring interact &rest body)
   "Define a function to be used in a *cvs* buffer.
@@ -698,6 +711,7 @@ clear what alternative to use.
 - NOARGS will get all the arguments from the *cvs* buffer and will
   always behave as if called interactively.
 - DOUBLE is the generic case."
+  (declare (debug (&define sexp lambda-list stringp ("interactive" interactive) def-body)))
   (let ((style (cvs-cdr fun))
        (fun (cvs-car fun)))
     (cond
@@ -732,7 +746,6 @@ before calling the real function `" (symbol-name fun-1) "'.\n")
             (cvs-mode! ',fun-1)))))
 
      (t (error "unknown style %s in `defun-cvs-mode'" style)))))
-(def-edebug-spec defun-cvs-mode (&define sexp lambda-list stringp ("interactive" interactive) def-body))
 
 (defun-cvs-mode cvs-mode-kill-process ()
   "Kill the temporary buffer and associated process."
@@ -766,6 +779,8 @@ TIN specifies an optional starting point."
        ;; fi == tin
        (cvs-fileinfo-update (ewoc-data tin) fi)
        (ewoc-invalidate c tin)
+       ;; Move cursor back to where it belongs.
+       (when (bolp) (cvs-move-to-goal-column))
        tin))))
 
 (defcustom cvs-cleanup-functions nil
@@ -879,7 +894,7 @@ and run `cvs-mode' on it.
 With a prefix argument, prompt for cvs FLAGS to use."
   (interactive
    (list (cvs-string->strings (read-string "Module(s): " (cvs-get-module)))
-        (read-file-name "CVS Checkout Directory: "
+        (read-directory-name "CVS Checkout Directory: "
                         nil default-directory nil)
         (cvs-add-branch-prefix
          (cvs-flags-query 'cvs-checkout-flags "cvs checkout flags"))))
@@ -906,7 +921,7 @@ With a prefix argument, prompt for cvs FLAGS to use."
           (not current-prefix-arg)
           (not (eq last-command-char ?\r)))
       default-directory
-    (read-file-name msg nil default-directory nil)))
+    (read-directory-name msg nil default-directory nil)))
 
 ;;;###autoload
 (defun cvs-quickdir (dir &optional flags noshow)
@@ -962,9 +977,11 @@ Optional argument NOSHOW if non-nil means not to display the buffer."
 (defun cvs-update (directory flags)
   "Run a `cvs update' in the current working DIRECTORY.
 Feed the output to a *cvs* buffer and run `cvs-mode' on it.
-With a prefix argument, prompt for a directory and cvs FLAGS to use.
+With a \\[universal-argument] prefix argument, prompt for a directory to use.
 A prefix arg >8 (ex: \\[universal-argument] \\[universal-argument]),
-  prevents reuse of an existing *cvs* buffer."
+  prevents reuse of an existing *cvs* buffer.
+The prefix is also passed to `cvs-flags-query' to select the FLAGS
+  passed to cvs."
   (interactive (list (cvs-query-directory "CVS Update (directory): ")
                     (cvs-flags-query 'cvs-update-flags "cvs update flags")))
   (when (eq flags t)
@@ -1052,7 +1069,7 @@ Full documentation is in the Texinfo file."
               ("" cvs-branch-prefix (cvs-secondary-branch-prefix
                                      ("->" cvs-secondary-branch-prefix))))
          " " cvs-mode-line-process))
-  (buffer-disable-undo (current-buffer))
+  (buffer-disable-undo)
   ;;(set (make-local-variable 'goal-column) cvs-cursor-column)
   (set (make-local-variable 'revert-buffer-function) 'cvs-mode-revert-buffer)
   (setq truncate-lines t)
@@ -1108,29 +1125,25 @@ Full documentation is in the Texinfo file."
 
 ;; Move around in the buffer
 
+(defun cvs-move-to-goal-column ()
+  (let* ((eol (line-end-position))
+        (fpos (next-single-property-change (point) 'cvs-goal-column nil eol)))
+    (when (< fpos eol)
+      (goto-char fpos))))
+
 (defun-cvs-mode cvs-mode-previous-line (arg)
   "Go to the previous line.
 If a prefix argument is given, move by that many lines."
   (interactive "p")
   (ewoc-goto-prev cvs-cookies arg)
-  (let ((fpos (next-single-property-change
-              (point) 'cvs-goal-column
-              (current-buffer) (line-end-position)))
-       (eol (line-end-position)))
-    (when (< fpos eol)
-      (goto-char fpos))))
+  (cvs-move-to-goal-column))
 
 (defun-cvs-mode cvs-mode-next-line (arg)
   "Go to the next line.
 If a prefix argument is given, move by that many lines."
   (interactive "p")
   (ewoc-goto-next cvs-cookies arg)
-  (let ((fpos (next-single-property-change
-              (point) 'cvs-goal-column
-              (current-buffer) (line-end-position)))
-       (eol (line-end-position)))
-    (when (< fpos eol)
-      (goto-char fpos))))
+  (cvs-move-to-goal-column))
 
 ;;;;
 ;;;; Mark handling
@@ -1355,7 +1368,8 @@ If FILE is non-nil, directory entries won't be selected."
 (defcustom cvs-mode-commit-hook nil
   "Hook run after setting up the commit buffer."
   :type 'hook
-  :options '(cvs-mode-diff))
+  :options '(cvs-mode-diff)
+  :group 'pcl-cvs)
 
 (defun cvs-mode-commit (setup)
   "Check in all marked files, or the current file.
@@ -1395,6 +1409,63 @@ The POSTPROC specified there (typically `log-edit') is then called,
     (cvs-mode-do "commit" (list* "-m" msg flags) 'commit)))
 
 
+;;;; Editing existing commit log messages.
+
+(defun cvs-edit-log-text-at-point ()
+  (save-excursion
+    (end-of-line)
+    (when (re-search-backward "^revision " nil t)
+      (forward-line 1)
+      (if (looking-at "date:") (forward-line 1))
+      (if (looking-at "branches:") (forward-line 1))
+      (buffer-substring
+       (point)
+       (if (re-search-forward
+           "^\\(-\\{28\\}\\|=\\{77\\}\\|revision [.0-9]+\\)$"
+           nil t)
+          (match-beginning 0)
+        (point))))))
+
+(defun cvs-mode-edit-log (rev &optional text)
+  "Edit the log message at point.
+This is best called from a `log-view-mode' buffer."
+  (interactive
+   (list
+    (or (cvs-mode! (lambda () (cvs-prefix-get 'cvs-branch-prefix)))
+       (read-string "Revision to edit: "))
+    (cvs-edit-log-text-at-point)))
+  ;; It seems that the save-excursion that happens if I use the better
+  ;; form of `(cvs-mode! (lambda ...))' screws up a couple things which
+  ;; end up being rather annoying (like log-edit-mode's message being
+  ;; displayed in the wrong minibuffer).
+  (cvs-mode!)
+  (let ((buf (cvs-temp-buffer "message" 'normal 'nosetup))
+       (lbd list-buffers-directory)
+       (setupfun (or (nth 2 (cdr (assoc "message" cvs-buffer-name-alist)))
+                     'log-edit)))
+    (funcall setupfun 'cvs-do-edit-log nil 'cvs-edit-log-filelist buf)
+    (when text (erase-buffer) (insert text))
+    (set (make-local-variable 'cvs-edit-log-revision) rev)
+    (set (make-local-variable 'cvs-minor-wrap-function) 'cvs-edit-log-minor-wrap)
+    (set (make-local-variable 'list-buffers-directory) lbd)
+    ;; (run-hooks 'cvs-mode-commit-hook)
+    ))
+
+(defun cvs-edit-log-minor-wrap (buf f)
+  (let ((cvs-ignore-marks-modif (cvs-mode-mark-get-modif "commit")))
+    (funcall f)))
+
+(defun cvs-edit-log-filelist ()
+  (cvs-mode-files nil nil :read-only t :file t :noquery t))
+
+(defun cvs-do-edit-log (rev)
+  "Do the actual commit, using the current buffer as the log message."
+  (interactive (list cvs-edit-log-revision))
+  (let ((msg (buffer-substring-no-properties (point-min) (point-max))))
+    (cvs-mode!)
+    (cvs-mode-do "admin" (list (concat "-m" rev ":" msg)) nil)))
+
+
 ;;;;
 ;;;; CVS Mode commands
 ;;;;
@@ -1676,6 +1747,12 @@ POSTPROC is a list of expressions to be evaluated at the very end (after
     ;; Save the relevant buffers
     (save-some-buffers nil (lambda () (cvs-is-within-p fis def-dir))))
   (unless (listp flags) (error "flags should be a list of strings"))
+  ;; Some w32 versions of CVS don't like an explicit . too much.
+  (when (and (car fis) (null (cdr fis))
+            (eq (cvs-fileinfo->type (car fis)) 'DIRCHANGE)
+            ;; (equal (cvs-fileinfo->file (car fis)) ".")
+            (equal (cvs-fileinfo->dir (car fis)) ""))
+    (setq fis nil))
   (let* ((cvs-buf (current-buffer))
         (single-dir (or (not (listp cvs-execute-single-dir))
                         (member cmd cvs-execute-single-dir)))
@@ -1821,7 +1898,7 @@ This command ignores files that are not flagged as `Unknown'."
 With a prefix, opens the buffer in an OTHER window."
   (interactive (list last-input-event current-prefix-arg))
   (when (ignore-errors (mouse-set-point e) t)  ;for invocation via the mouse
-    (unless (memq (get-text-property (point) 'face)
+    (unless (memq (get-text-property (1- (line-end-position)) 'font-lock-face)
                  '(cvs-header-face cvs-filename-face))
       (error "Not a file name")))
   (cvs-mode!
@@ -1857,7 +1934,10 @@ The file is removed and `cvs update FILE' is run."
   (interactive (list nil));; (cvs-flags-query 'cvs-undo-flags "undo flags")
   (if current-prefix-arg (call-interactively 'cvs-mode-revert-to-rev)
     (let* ((fis (cvs-do-removal 'undo "update" 'all))
-          (removedp (lambda (fi) (eq (cvs-fileinfo->type fi) 'REMOVED)))
+          (removedp (lambda (fi)
+                      (or (eq (cvs-fileinfo->type fi) 'REMOVED)
+                          (and (eq (cvs-fileinfo->type fi) 'CONFLICT)
+                               (eq (cvs-fileinfo->subtype fi) 'REMOVED)))))
           (fis-split (cvs-partition removedp fis))
           (fis-removed (car fis-split))
           (fis-other (cdr fis-split)))
@@ -1932,9 +2012,10 @@ Returns a list of FIS that should be `cvs remove'd."
         (tmpbuf (cvs-temp-buffer)))
     (when (and (not silent) (equal cvs-confirm-removals 'list))
       (with-current-buffer tmpbuf
-       (cvs-insert-strings (mapcar 'cvs-fileinfo->full-path fis))
-       (cvs-pop-to-buffer-same-frame (current-buffer))
-       (shrink-window-if-larger-than-buffer)))
+       (let ((inhibit-read-only t))
+         (cvs-insert-strings (mapcar 'cvs-fileinfo->full-path fis))
+         (cvs-pop-to-buffer-same-frame (current-buffer))
+         (shrink-window-if-larger-than-buffer))))
     (if (not (or silent
                 (unwind-protect
                     (yes-or-no-p (format "Delete %d files? " (length files)))
@@ -2144,7 +2225,7 @@ The exact behavior is determined also by `cvs-dired-use-hook'."
                 (pop flags))
               ;; don't parse output we don't understand.
               (member (car flags) cvs-parse-known-commands)))
-    (save-excursion
+    (save-current-buffer
       (let ((buffer (current-buffer))
            (dir default-directory)
            (cvs-from-vc t))
@@ -2156,6 +2237,13 @@ The exact behavior is determined also by `cvs-dired-use-hook'."
            (let ((subdir (substring dir (length default-directory))))
              (set-buffer buffer)
              (set (make-local-variable 'cvs-buffer) cvs-buf)
+             ;; `cvs -q add file' produces no useful output :-(
+             (when (and (equal (car flags) "add")
+                        (goto-char (point-min))
+                        (looking-at ".*to add this file permanently\n\\'"))
+               (insert "cvs add: scheduling file `"
+                       (file-name-nondirectory file)
+                       "' for addition\n"))
              ;; VC never (?) does `cvs -n update' so dcd=nil
              ;; should probably always be the right choice.
              (cvs-parse-process nil subdir))))))))