(custom-face-value-create): If face name doesn't end with "face", add
[bpt/emacs.git] / lisp / vc.el
index 4d12657..9b52383 100644 (file)
@@ -5,7 +5,7 @@
 ;; Author:     FSF (see below for full credits)
 ;; Maintainer: Andre Spiegel <spiegel@gnu.org>
 
-;; $Id: vc.el,v 1.276 2000/10/03 12:24:15 spiegel Exp $
+;; $Id: vc.el,v 1.284 2000/10/26 20:53:11 monnier Exp $
 
 ;; This file is part of GNU Emacs.
 
@@ -33,7 +33,7 @@
 ;;   Paul Eggert <eggert@twinsun.com>
 ;;   Sebastian Kremer <sk@thp.uni-koeln.de>
 ;;   Martin Lorentzson <martinl@gnu.org>
-;;   Dave Love <d.love@gnu.org>
+;;   Dave Love <fx@gnu.org>
 ;;   Stefan Monnier <monnier@cs.yale.edu>
 ;;   Andre Spiegel <spiegel@gnu.org>
 ;;   Richard Stallman <rms@gnu.org>
@@ -479,18 +479,18 @@ The keys are \(BUFFER . BACKEND\).  See also `vc-annotate-get-backend'.")
   (setq vc-comment-ring (make-ring vc-maximum-comment-ring-size)))
 
 (defmacro with-vc-properties (file form settings)
-  "Execute FORM, then set per-file properties for FILE, but only those
-that have not been set during the execution of FORM.  SETTINGS is a list 
-of two-element lists, each of which has the form (PROPERTY VALUE)."
+  "Execute FORM, then set per-file properties for FILE,
+but only those that have not been set during the execution of FORM.
+SETTINGS is a list of two-element lists, each of which has the
+  form (PROPERTY . VALUE)."
   `(let ((vc-touched-properties (list t))
         (filename ,file))
      ,form
      (mapcar (lambda (setting)
-              (let ((property (nth 0 setting))
-                    (value (nth 1 setting)))
+              (let ((property (car setting)))
                 (unless (memq property vc-touched-properties)
-                  (put (intern filename vc-file-prop-obarray) 
-                       property value))))
+                  (put (intern filename vc-file-prop-obarray)
+                       property (cdr setting)))))
             ,settings)))
 
 ;; Random helper functions
@@ -517,6 +517,7 @@ somebody else, signal error."
      (save-excursion
        ,@body)
      (vc-checkin file nil ,comment)))
+(put 'with-vc-file 'indent-function 1)
 
 ;;;###autoload
 (defmacro edit-vc-file (file comment &rest body)
@@ -529,6 +530,7 @@ However, before executing BODY, find FILE, and after BODY, save buffer."
     (set-buffer (find-file-noselect ,file))
     ,@body
     (save-buffer)))
+(put 'edit-vc-file 'indent-function 1)
 
 (defun vc-ensure-vc-buffer ()
   "Make sure that the current buffer visits a version-controlled file."
@@ -802,7 +804,7 @@ NOT-URGENT means it is ok to continue if the user says not to save."
       (let ((unchanged (vc-call workfile-unchanged-p file)))
         (vc-file-setprop file 'vc-checkout-time (if unchanged lastmod 0))
         unchanged))))
-      
+
 (defun vc-default-workfile-unchanged-p (file)
   "Default check whether FILE is unchanged: diff against master version."
   (zerop (vc-call diff file (vc-workfile-version file))))
@@ -835,7 +837,7 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
        (if (buffer-modified-p)
            (or (y-or-n-p "Operate on disk file, keeping modified buffer? ")
                (error "Aborted")))))
-    
+
     ;; Do the right thing
     (if (not (vc-registered file))
        (vc-register verbose comment)
@@ -862,7 +864,7 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
         (t
          ;; do nothing
          (message "%s is up-to-date" file))))
-       
+
        ;; Abnormal: edited but read-only
        ((and visited (eq state 'edited) buffer-read-only)
        ;; Make the file+buffer read-write.  If the user really wanted to
@@ -871,7 +873,7 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
        (set-file-modes buffer-file-name
                        (logior (file-modes buffer-file-name) 128))
        (toggle-read-only -1))
-       
+
        ;; edited
        ((eq state 'edited)
        (cond
@@ -896,7 +898,7 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
              (if (member vsym vc-handled-backends)
                  (vc-transfer-file file vsym)
                (vc-checkin file version comment)))))))
-       
+
        ;; locked by somebody else
        ((stringp state)
        (if comment
@@ -906,7 +908,7 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
                        (if verbose (read-string "Version to steal: ")
                          (vc-workfile-version file))
                       state))
-       
+
        ;; needs-patch
        ((eq state 'needs-patch)
        (if (yes-or-no-p (format
@@ -917,7 +919,7 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
                   (yes-or-no-p "Lock this version? "))
              (vc-checkout file t)
            (error "Aborted"))))
-       
+
        ;; needs-merge
        ((eq state 'needs-merge)
        (if (yes-or-no-p (format
@@ -925,7 +927,7 @@ If VERBOSE is non-nil, query the user rather than using default parameters."
                          (file-name-nondirectory file)))
            (vc-maybe-resolve-conflicts file (vc-call merge-news file))
          (error "Aborted")))
-       
+
        ;; unlocked-changes
        ((eq state 'unlocked-changes)
        (if (not visited) (find-file-other-window file))
@@ -1060,7 +1062,7 @@ first backend that could register the file is used."
           (not (file-exists-p buffer-file-name)))
       (set-buffer-modified-p t))
   (vc-buffer-sync)
-  
+
   (vc-start-entry buffer-file-name
                   (if set-version
                       (read-string (format "Initial version level for %s: "
@@ -1084,7 +1086,7 @@ first backend that could register the file is used."
 
 (defun vc-responsible-backend (file &optional register)
   "Return the name of a backend system that is responsible for FILE.
-The optional argument REGISTER means that a backend suitable for 
+The optional argument REGISTER means that a backend suitable for
 registration should be found.
 
 If REGISTER is nil, then if FILE is already registered, return the
@@ -1112,7 +1114,7 @@ be registered."
        (if (not register)
            ;; if this is not for registration, the first backend must do
            (car vc-handled-backends)
-         ;; for registration, we need to find a new backend that 
+         ;; for registration, we need to find a new backend that
          ;; could register FILE
          (dolist (backend vc-handled-backends)
            (and (not (vc-call-backend backend 'registered file))
@@ -1121,7 +1123,7 @@ be registered."
          (error "No backend that could register")))))
 
 (defun vc-default-responsible-p (backend file)
-  "Indicate whether BACKEND is reponsible for FILE.  
+  "Indicate whether BACKEND is reponsible for FILE.
 The default is to return nil always."
   nil)
 
@@ -1166,15 +1168,15 @@ rather than user editing!"
   (vc-dired-resynch-file file))
 
 (defun vc-start-entry (file rev comment initial-contents msg action &optional after-hook)
-  "Accept a comment for an operation on FILE revision REV.  
+  "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.  If COMMENT is a string and
 INITIAL-CONTENTS is non-nil, then COMMENT is used as the initial
 contents of the log entry buffer.  If COMMENT is a string and
 INITIAL-CONTENTS is nil, do action immediately as if the user had
 entered COMMENT.  If COMMENT is t, also do action immediately with an
-empty comment.  Remember the file's buffer in `vc-parent-buffer' 
-\(current one if no file).  AFTER-HOOK specifies the local value 
+empty comment.  Remember the file's buffer in `vc-parent-buffer'
+\(current one if no file).  AFTER-HOOK specifies the local value
 for vc-log-operation-hook."
   (let ((parent (or (and file (get-file-buffer file)) (current-buffer))))
     (if vc-before-checkin-hook
@@ -1195,14 +1197,12 @@ for vc-log-operation-hook."
        (setq vc-log-after-operation-hook after-hook))
     (setq vc-log-operation action)
     (setq vc-log-version rev)
-    (erase-buffer)
-    (if (eq comment t)
-       (vc-finish-logentry t)
-      (if comment
-         (insert comment))
-      (if (and comment (not initial-contents))
-         (vc-finish-logentry nil)
-       (message "%s  Type C-c C-c when done" msg)))))
+    (when comment
+      (erase-buffer)
+      (when (stringp comment) (insert comment)))
+    (if (or (not comment) initial-contents)
+       (message "%s  Type C-c C-c when done" msg)
+      (vc-finish-logentry (eq comment t)))))
 
 (defun vc-checkout (file &optional writable rev)
   "Retrieve a copy of the revision REV of FILE.
@@ -1210,10 +1210,9 @@ If WRITABLE is non-nil, make sure the retrieved file is writable.
 REV defaults to the latest revision."
   (and writable
        (not rev)
-       (vc-call make-version-backups file)
+       (vc-call make-version-backups-p file)
        (vc-up-to-date-p file)
-       (copy-file file (vc-version-backup-file-name file)
-                 'ok-if-already-exists 'keep-date))
+       (vc-make-version-backup file))
   (with-vc-properties
    file
    (condition-case err
@@ -1224,13 +1223,13 @@ REV defaults to the latest revision."
        (let ((buf (get-file-buffer file)))
          (when buf (with-current-buffer buf (toggle-read-only -1)))))
       (signal (car err) (cdr err))))
-   `((vc-state ,(if (or (eq (vc-checkout-model file) 'implicit)
-                       (not writable))
-                   (if (vc-call latest-on-branch-p file)
-                       'up-to-date
-                     'needs-patch)
-                 'edited))
-     (vc-checkout-time ,(nth 5 (file-attributes file)))))
+   `((vc-state ,(if (or (eq (vc-checkout-model file) 'implicit)
+                         (not writable))
+                     (if (vc-call latest-on-branch-p file)
+                         'up-to-date
+                       'needs-patch)
+                   'edited))
+     (vc-checkout-time ,(nth 5 (file-attributes file)))))
   (vc-resynch-buffer file t t))
 
 (defun vc-steal-lock (file rev owner)
@@ -1256,10 +1255,10 @@ REV defaults to the latest revision."
 (defun vc-finish-steal (file version)
   ;; This is called when the notification has been sent.
   (message "Stealing lock on %s..." file)
-  (with-vc-properties 
+  (with-vc-properties
    file
    (vc-call steal-lock file version)
-   `((vc-state edited)))
+   `((vc-state edited)))
   (vc-resynch-buffer file t t)
   (message "Stealing lock on %s...done" file))
 
@@ -1284,16 +1283,16 @@ Runs the normal hook `vc-checkin-hook'."
      ;; RCS 5.7 gripes about white-space-only comments too.
      (or (and comment (string-match "[^\t\n ]" comment))
         (setq comment "*** empty log message ***"))
-     (with-vc-properties 
+     (with-vc-properties
       file
       ;; Change buffers to get local value of vc-checkin-switches.
       (with-current-buffer (or (get-file-buffer file) (current-buffer))
        (let ((backup-file (vc-version-backup-file file)))
          (vc-call checkin file rev comment)
          (if backup-file (delete-file backup-file))))
-      `((vc-state up-to-date)
-       (vc-checkout-time ,(nth 5 (file-attributes file)))
-       (vc-workfile-version nil)))
+      `((vc-state up-to-date)
+       (vc-checkout-time ,(nth 5 (file-attributes file)))
+       (vc-workfile-version nil)))
      (message "Checking in %s...done" file))
    'vc-checkin-hook))
 
@@ -1390,7 +1389,7 @@ May be useful as a `vc-checkin-hook' to update change logs automatically."
                    (bury-buffer)
                    (pop-to-buffer tmp-vc-parent-buffer))))
     ;; Now make sure we see the expanded headers
-    (if log-file 
+    (if log-file
        (vc-resynch-buffer log-file vc-keep-workfiles t))
     (if vc-dired-mode
       (dired-move-to-filename))
@@ -1425,7 +1424,7 @@ May be useful as a `vc-checkin-hook' to update change logs automatically."
   (vc-previous-comment (- arg)))
 
 (defun vc-comment-search-reverse (str &optional stride)
-  "Searches backwards through comment history for substring match."
+  "Search backwards through comment history for substring match."
   ;; Why substring rather than regexp ?   -sm
   (interactive
    (list (read-string "Comment substring: " nil nil vc-last-comment-match)))
@@ -1443,7 +1442,7 @@ May be useful as a `vc-checkin-hook' to update change logs automatically."
     (vc-previous-comment 0)))
 
 (defun vc-comment-search-forward (str)
-  "Searches forwards through comment history for substring match."
+  "Search forwards through comment history for substring match."
   (interactive
    (list (read-string "Comment substring: " nil nil vc-last-comment-match)))
   (vc-comment-search-reverse str -1))
@@ -1531,11 +1530,16 @@ files in or below it."
     (if (string-equal rel2 "")
        (setq rel2 nil))
     (let ((file-rel1 (vc-version-backup-file file rel1))
-         (file-rel2 (if (not rel2) 
-                        file 
+         (file-rel2 (if (not rel2)
+                        file
                       (vc-version-backup-file file rel2))))
       (if (and file-rel1 file-rel2)
-         (vc-do-command t 1 "diff" nil diff-switches file-rel1 file-rel2)
+         (apply 'vc-do-command t 1 "diff" nil
+                (append (if (listp diff-switches)
+                            diff-switches
+                          (list diff-switches))
+                        (list (file-relative-name file-rel1)
+                              (file-relative-name file-rel2))))
        (cd (file-name-directory file))
        (vc-call diff file rel1 rel2))))
   (if (and (zerop (buffer-size))
@@ -1564,13 +1568,17 @@ 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 workfile version): ")
   (vc-ensure-vc-buffer)
-  (let* ((version (if (string-equal rev "")
-                     (vc-workfile-version buffer-file-name)
+  (let* ((file buffer-file-name)
+        (version (if (string-equal rev "")
+                     (vc-workfile-version file)
                    rev))
-        (filename (concat buffer-file-name ".~" version "~")))
-    (or (file-exists-p filename)
-       (vc-call checkout buffer-file-name nil version filename))
-    (find-file-other-window filename)))
+        (automatic-backup (vc-version-backup-file-name file version))
+         (manual-backup (vc-version-backup-file-name file version 'manual)))
+    (unless (file-exists-p manual-backup)
+      (if (file-exists-p automatic-backup)
+          (rename-file automatic-backup manual-backup nil)
+        (vc-call checkout file nil version manual-backup)))
+    (find-file-other-window manual-backup)))
 
 ;; Header-insertion code
 
@@ -1648,7 +1656,7 @@ See Info node `Merging'."
           "File must be checked out for merging.  Check out now? ")
          (vc-checkout file t)
        (error "Merge aborted"))))
-    (setq first-version 
+    (setq first-version
          (read-string (concat "Branch or version to merge from "
                               "(default: news on current branch): ")))
     (if (string= first-version "")
@@ -1658,8 +1666,8 @@ See Info node `Merging'."
       (if (not (vc-find-backend-function backend 'merge))
          (error "Sorry, merging is not implemented for %s" backend)
        (if (not (vc-branch-p first-version))
-           (setq second-version 
-                 (read-string "Second 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.
@@ -2180,16 +2188,21 @@ changes found in the master file; use \\[universal-argument] \\[vc-next-action]
        (obuf (current-buffer))
        status)
     (unless (vc-workfile-unchanged-p file)
-      (setq status (vc-diff nil t))
-      (vc-exec-after `(message nil))
-      (when status
-       (unwind-protect
-           (if (not (yes-or-no-p "Discard changes? "))
+      ;; 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
+      ;; moving her mouse to answer the yes-or-no-p question.
+      (let ((win (save-selected-window
+                  (setq status (vc-diff nil t)) (selected-window))))
+       (vc-exec-after `(message nil))
+       (when status
+         (unwind-protect
+             (unless (yes-or-no-p "Discard changes? ")
                (error "Revert canceled"))
-         (if (and (window-dedicated-p (selected-window))
-                  (one-window-p t))
-             (make-frame-invisible)
-           (delete-window)))))
+           (select-window win)
+           (if (one-window-p t)
+               (if (window-dedicated-p (selected-window))
+                   (make-frame-invisible))
+             (delete-window))))))
     (set-buffer obuf)
     ;; Do the reverting
     (message "Reverting %s..." file)
@@ -2200,10 +2213,14 @@ changes found in the master file; use \\[universal-argument] \\[vc-next-action]
   "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."
-  (when (vc-call make-version-backups file)
+  (when (vc-call make-version-backups-p file)
     (let ((backup-file (vc-version-backup-file-name file rev)))
-      (and (file-exists-p backup-file)
-          backup-file))))
+      (if (file-exists-p backup-file)
+          backup-file
+        ;; there is no automatic backup, but maybe the user made one manually
+        (setq backup-file (vc-version-backup-file-name file rev 'manual))
+        (if (file-exists-p backup-file)
+            backup-file)))))
 
 (defun vc-revert-file (file)
   "Revert FILE back to the version it was based on."
@@ -2213,9 +2230,9 @@ return the name of it; otherwise return nil."
      (if (not backup-file)
         (vc-call revert file)
        (copy-file backup-file file 'ok-if-already-exists 'keep-date)
-       (delete-file backup-file)))
-   `((vc-state up-to-date)
-     (vc-checkout-time ,(nth 5 (file-attributes file)))))
+       (vc-delete-automatic-version-backups file)))
+   `((vc-state up-to-date)
+     (vc-checkout-time ,(nth 5 (file-attributes file)))))
   (vc-resynch-buffer file t t))
 
 ;;;###autoload
@@ -2244,11 +2261,11 @@ A prefix argument NOREVERT means do not revert the buffer afterwards."
       (with-vc-properties
        file
        (vc-call cancel-version file norevert)
-       `((vc-state ,(if norevert 'edited 'up-to-date))
-        (vc-checkout-time ,(if norevert 
-                               0 
+       `((vc-state ,(if norevert 'edited 'up-to-date))
+        (vc-checkout-time . ,(if norevert
+                               0
                              (nth 5 (file-attributes file))))
-        (vc-workfile-version nil)))
+        (vc-workfile-version nil)))
       (message "Removing last change from %s...done" file)
 
       (cond
@@ -2297,12 +2314,12 @@ To get a prompt, use a prefix argument."
             nil t nil nil (downcase (symbol-name def))))))
        (t def))))))
   (unless (eq backend (vc-backend file))
-    (unless (vc-call-backend backend 'registered file)
-      (error "%s is not registered in %s" file backend))
     (vc-file-clearprops file)
     (vc-file-setprop file 'vc-backend backend)
     ;; Force recomputation of the state
-    (vc-call-backend backend 'registered file)
+    (unless (vc-call-backend backend 'registered file)
+      (vc-file-clearprops file)
+      (error "%s is not registered in %s" file backend))
     (vc-mode-line file)))
 
 ;;;autoload
@@ -2615,7 +2632,7 @@ colors. `vc-annotate-background' specifies the background color."
     (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 
+    (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)))