Merge from trunk.
[bpt/emacs.git] / lisp / vc / vc.el
index 3809b5b..ab7e587 100644 (file)
@@ -1,6 +1,6 @@
 ;;; vc.el --- drive a version-control system from within Emacs
 
-;; Copyright (C) 1992-1998, 2000-201 Free Software Foundation, Inc.
+;; Copyright (C) 1992-1998, 2000-2012 Free Software Foundation, Inc.
 
 ;; Author:     FSF (see below for full credits)
 ;; Maintainer: Andre Spiegel <spiegel@gnu.org>
 ;; * working-revision (file)
 ;;
 ;;   Return the working revision of FILE.  This is the revision fetched
-;;   by the last checkout or upate, not necessarily the same thing as the
+;;   by the last checkout or update, not necessarily the same thing as the
 ;;   head or tip revision.  Should return "0" for a file added but not yet
 ;;   committed.
 ;;
 ;;   buffer, if one is present, instead of adding to the ChangeLog.
 ;;
 ;; - When vc-next-action calls vc-checkin it could pre-fill the
-;;   *VC-log* buffer with some obvious items: the list of files that
+;;   *vc-log* buffer with some obvious items: the list of files that
 ;;   were added, the list of files that were removed.  If the diff is
 ;;   available, maybe it could even call something like
 ;;   `diff-add-change-log-entries-other-window' to create a detailed
   :type 'boolean
   :group 'vc)
 
+(make-obsolete-variable 'vc-initial-comment "it has no effect." "23.2")
+
 (defcustom vc-default-init-revision "1.1"
   "A string used as the default revision number when a new file is registered.
 This can be overridden by giving a prefix argument to \\[vc-register].  This
@@ -775,6 +777,12 @@ See `run-hooks'."
   :type 'hook
   :group 'vc)
 
+(defcustom vc-revert-show-diff t
+  "If non-nil, `vc-revert' shows a `vc-diff' buffer before querying."
+  :type 'boolean
+  :group 'vc
+  :version "24.1")
+
 ;; Header-insertion hair
 
 (defcustom vc-static-header-alist
@@ -839,7 +847,7 @@ been updated to their corresponding values."
        (if (file-directory-p file)
           (dolist (buffer (buffer-list))
             (let ((fname (buffer-file-name buffer)))
-              (when (and fname (vc-string-prefix-p file fname))
+              (when (and fname (string-prefix-p file fname))
                 (push fname flist))))
         (push file flist)))
      ,form
@@ -892,7 +900,7 @@ use."
               (lambda (arg)
                 (message "arg %s" arg)
                 (and (file-directory-p arg)
-                     (vc-string-prefix-p (expand-file-name arg) def-dir)))))))
+                     (string-prefix-p (expand-file-name arg) def-dir)))))))
           (let ((default-directory repo-dir))
        (vc-call-backend bk 'create-repo))
       (throw 'found bk))))
@@ -943,13 +951,13 @@ Within directories, only files already under version control are noticed."
 (defun vc-deduce-fileset (&optional observer allow-unregistered
                                    state-model-only-files)
   "Deduce a set of files and a backend to which to apply an operation.
-
 Return (BACKEND FILESET FILESET-ONLY-FILES STATE CHECKOUT-MODEL).
-If we're in VC-dir mode, the fileset is the list of marked files.
-Otherwise, if we're looking at a buffer visiting a version-controlled file,
-the fileset is a singleton containing this file.
-If none of these conditions is met, but ALLOW_UNREGISTERED is on and the
-visited file is not registered, return a singleton fileset containing it.
+
+If we're in VC-dir mode, FILESET is the list of marked files.
+Otherwise, if in a buffer visiting a version-controlled file,
+FILESET is a single-file fileset containing that file.
+Otherwise, if ALLOW-UNREGISTERED is non-nil and the visited file
+is unregistered, FILESET is a single-file fileset containing it.
 Otherwise, throw an error.
 
 STATE-MODEL-ONLY-FILES if non-nil, means that the caller needs
@@ -1040,34 +1048,27 @@ current buffer."
 ;;;###autoload
 (defun vc-next-action (verbose)
   "Do the next logical version control operation on the current fileset.
-This requires that all files in the fileset be in the same state.
-
-For locking systems:
-   If every file is not already registered, this registers each for version
-control.
-   If every file is registered and not locked by anyone, this checks out
-a writable and locked file of each ready for editing.
-   If every file is checked out and locked by the calling user, this
-first checks to see if each file has changed since checkout.  If not,
-it performs a revert on that file.
-   If every 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 each changed file is left in place afterwards.
-   If the affected file is registered and locked by someone else, you are
-given the option to steal the lock(s).
-
-For merging systems:
-   If every file is not already registered, this registers each one for version
-control.  This does an add, but not a commit.
-   If every file is added but not committed, each one is committed.
-   If every working file is changed, but the corresponding repository file is
-unchanged, 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 logmessage as change commentary.  A writable file is retained.
-   If the repository file is changed, you are asked if you want to
-merge in the changes into your working copy."
+This requires that all files in the current VC fileset be in the
+same state.  If not, signal an error.
+
+For merging-based version control systems:
+  If every file in the VC fileset is not registered for version
+   control, register the fileset (but don't commit).
+  If every work file in the VC fileset is added or changed, pop
+   up a *vc-log* buffer to commit the fileset.
+  For a centralized version control system, if any work file in
+   the VC fileset is out of date, offer to update the fileset.
+
+For old-style locking-based version control systems, like RCS:
+  If every file is not registered, register the file(s).
+  If every file is registered and unlocked, check out (lock)
+   the file(s) for editing.
+  If every file is locked by you and has changes, pop up a
+   *vc-log* buffer to check in the changes.  If the variable
+   `vc-keep-workfiles' is non-nil (the default), leave a
+   read-only copy of each changed file after checking in.
+  If every file is locked by you and unchanged, unlock them.
+  If every file is locked by someone else, offer to steal the lock."
   (interactive "P")
   (let* ((vc-fileset (vc-deduce-fileset nil t 'state-model-only-files))
          (backend (car vc-fileset))
@@ -1112,9 +1113,13 @@ merge in the changes into your working copy."
      ;; Files have local changes
      ((vc-compatible-state state 'edited)
       (let ((ready-for-commit files))
-       ;; If files are edited but read-only, give user a chance to correct
+       ;; If files are edited but read-only, give user a chance to correct.
        (dolist (file files)
-         (unless (file-writable-p file)
+         ;; If committing a mix of removed and edited files, the
+         ;; fileset has state = 'edited.  Rather than checking the
+         ;; state of each individual file in the fileset, it seems
+         ;; simplest to just check if the file exists.  Bug#9781.
+         (when (and (file-exists-p file) (not (file-writable-p file)))
            ;; Make the file+buffer read-write.
            (unless (y-or-n-p (format "%s is edited but read-only; make it writable and continue? " file))
              (error "Aborted"))
@@ -1408,7 +1413,7 @@ Runs the normal hooks `vc-before-checkin-hook' and `vc-checkin-hook'."
    (vc-start-logentry
     files comment initial-contents
     "Enter a change comment."
-    "*VC-log*"
+    "*vc-log*"
     (lambda ()
       (vc-call-backend backend 'log-edit-mode))
     (lexical-let ((rev rev))
@@ -1534,10 +1539,13 @@ to override the value of `vc-diff-switches' and `diff-switches'."
 (defvar vc-diff-added-files nil
   "If non-nil, diff added files by comparing them to /dev/null.")
 
-(defun vc-diff-internal (async vc-fileset rev1 rev2 &optional verbose)
+(defun vc-diff-internal (async vc-fileset rev1 rev2 &optional verbose buffer)
   "Report diffs between two revisions of a fileset.
-Diff output goes to the *vc-diff* buffer.  The function
-returns t if the buffer had changes, nil otherwise."
+Output goes to the buffer BUFFER, which defaults to *vc-diff*.
+BUFFER, if non-nil, should be a buffer or a buffer name.
+Return t if the buffer had changes, nil otherwise."
+  (unless buffer
+    (setq buffer "*vc-diff*"))
   (let* ((files (cadr vc-fileset))
         (messages (cons (format "Finding changes in %s..."
                                  (vc-delistify files))
@@ -1549,7 +1557,7 @@ returns t if the buffer had changes, nil otherwise."
         ;; be to call the back end separately for each file.
         (coding-system-for-read
          (if files (vc-coding-system-for-diff (car files)) 'undecided)))
-    (vc-setup-buffer "*vc-diff*")
+    (vc-setup-buffer buffer)
     (message "%s" (car messages))
     ;; Many backends don't handle well the case of a file that has been
     ;; added but not yet committed to the repo (notably CVS and Subversion).
@@ -1574,13 +1582,13 @@ returns t if the buffer had changes, nil otherwise."
                 (error "No revisions of %s exist" file)
               ;; We regard this as "changed".
               ;; Diff it against /dev/null.
-              (apply 'vc-do-command "*vc-diff*"
+              (apply 'vc-do-command buffer
                      1 "diff" file
                      (append (vc-switches nil 'diff) '("/dev/null"))))))
         (setq files (nreverse filtered))))
     (let ((vc-disable-async-diff (not async)))
-      (vc-call-backend (car vc-fileset) 'diff files rev1 rev2 "*vc-diff*"))
-    (set-buffer "*vc-diff*")
+      (vc-call-backend (car vc-fileset) 'diff files rev1 rev2 buffer))
+    (set-buffer buffer)
     (if (and (zerop (buffer-size))
              (not (get-buffer-process (current-buffer))))
         ;; Treat this case specially so as not to pop the buffer.
@@ -1596,10 +1604,13 @@ returns t if the buffer had changes, nil otherwise."
       ;; bindings are nicer for read only buffers. pcl-cvs does the
       ;; same thing.
       (setq buffer-read-only t)
-      (vc-exec-after `(vc-diff-finish ,(current-buffer) ',(when verbose
-                                                            messages)))
       ;; Display the buffer, but at the end because it can change point.
       (pop-to-buffer (current-buffer))
+      ;; The diff process may finish early, so call `vc-diff-finish'
+      ;; after `pop-to-buffer'; the former assumes the diff buffer is
+      ;; shown in some window.
+      (vc-exec-after `(vc-diff-finish ,(current-buffer)
+                                     ',(when verbose messages)))
       ;; In the async case, we return t even if there are no differences
       ;; because we don't know that yet.
       t)))
@@ -1867,9 +1878,10 @@ The headers are reset to their non-expanded form."
     (vc-start-logentry
      files oldcomment t
      "Enter a replacement change comment."
-     "*VC-log*"
+     "*vc-log*"
      (lambda () (vc-call-backend backend 'log-edit-mode))
-     (lexical-let ((rev rev))
+     (lexical-let ((rev rev)
+                   (backend backend))
        (lambda (files comment)
          (vc-call-backend backend
                           'modify-change-comment files rev comment))))))
@@ -1877,6 +1889,7 @@ The headers are reset to their non-expanded form."
 ;;;###autoload
 (defun vc-merge ()
   "Perform a version control merge operation.
+You must be visiting a version controlled file, or in a `vc-dir' buffer.
 On a distributed version control system, this runs a \"merge\"
 operation to incorporate changes from another branch onto the
 current branch, prompting for an argument list.
@@ -1909,7 +1922,7 @@ changes from the current branch."
          (setq first-revision
                (vc-read-revision
                 (concat "Merge " file
-                        "from branch or revision "
+                        " from branch or revision "
                         "(default news on current branch): ")
                 (list file)
                 backend))
@@ -2256,11 +2269,12 @@ This asks for confirmation if the buffer contents are not identical
 to the working revision (except for keyword expansion)."
   (interactive)
   (let* ((vc-fileset (vc-deduce-fileset))
-        (files (cadr vc-fileset)))
-    ;; If any of the files is visited by the current buffer, make
-    ;; sure buffer is saved.  If the user says `no', abort since
-    ;; we cannot show the changes and ask for confirmation to
-    ;; discard them.
+        (files (cadr vc-fileset))
+        (queried nil)
+        diff-buffer)
+    ;; If any of the files is visited by the current buffer, make sure
+    ;; buffer is saved.  If the user says `no', abort since we cannot
+    ;; show the changes and ask for confirmation to discard them.
     (when (or (not files) (memq (buffer-file-name) files))
       (vc-buffer-sync nil))
     (dolist (file files)
@@ -2268,20 +2282,28 @@ to the working revision (except for keyword expansion)."
        (when (and buf (buffer-modified-p buf))
          (error "Please kill or save all modified buffers before reverting")))
       (when (vc-up-to-date-p file)
-       (unless (yes-or-no-p (format "%s seems up-to-date.  Revert anyway? " file))
+       (if (yes-or-no-p (format "%s seems up-to-date.  Revert anyway? " file))
+           (setq queried t)
          (error "Revert canceled"))))
-    (when (vc-diff-internal vc-allow-async-revert vc-fileset nil nil)
-      (unless (yes-or-no-p
-              (format "Discard changes in %s? "
-                      (let ((str (vc-delistify files))
-                            (nfiles (length files)))
-                        (if (< (length str) 50)
-                            str
-                          (format "%d file%s" nfiles
-                                  (if (= nfiles 1) "" "s"))))))
-       (error "Revert canceled"))
-      (delete-windows-on "*vc-diff*")
-      (kill-buffer "*vc-diff*"))
+    (unwind-protect
+       (when (if vc-revert-show-diff
+                 (progn
+                   (setq diff-buffer (generate-new-buffer-name "*vc-diff*"))
+                   (vc-diff-internal vc-allow-async-revert vc-fileset
+                                     nil nil nil diff-buffer))
+               ;; Avoid querying the user again.
+               (null queried))
+         (unless (yes-or-no-p
+                  (format "Discard changes in %s? "
+                          (let ((str (vc-delistify files))
+                                (nfiles (length files)))
+                            (if (< (length str) 50)
+                                str
+                              (format "%d file%s" nfiles
+                                      (if (= nfiles 1) "" "s"))))))
+           (error "Revert canceled")))
+      (when diff-buffer
+       (quit-windows-on diff-buffer t)))
     (dolist (file files)
       (message "Reverting %s..." (vc-delistify files))
       (vc-revert-file file)
@@ -2327,8 +2349,7 @@ depending on the underlying version-control system."
     ;; Display changes
     (unless (yes-or-no-p "Discard these revisions? ")
       (error "Rollback canceled"))
-    (delete-windows-on "*vc-diff*")
-    (kill-buffer"*vc-diff*")
+    (quit-windows-on "*vc-diff*" t)
     ;; Do the actual reversions
     (message "Rolling back %s..." (vc-delistify files))
     (with-vc-properties
@@ -2346,6 +2367,7 @@ depending on the underlying version-control system."
 ;;;###autoload
 (defun vc-pull (&optional arg)
   "Update the current fileset or branch.
+You must be visiting a version controlled file, or in a `vc-dir' buffer.
 On a distributed version control system, this runs a \"pull\"
 operation to update the current branch, prompting for an argument
 list if required.  Optional prefix ARG forces a prompt.
@@ -2406,7 +2428,7 @@ its name; otherwise return nil."
    (list file)
    (let ((backup-file (vc-version-backup-file file)))
      (when backup-file
-       (copy-file backup-file file 'ok-if-already-exists 'keep-date)
+       (copy-file backup-file file 'ok-if-already-exists)
        (vc-delete-automatic-version-backups file))
      (vc-call revert file backup-file))
    `((vc-state . up-to-date)
@@ -2664,7 +2686,7 @@ log entries should be gathered."
       (substring rev 0 index))))
 
 (defun vc-default-responsible-p (backend file)
-  "Indicate whether BACKEND is reponsible for FILE.
+  "Indicate whether BACKEND is responsible for FILE.
 The default is to return nil always."
   nil)
 
@@ -2787,11 +2809,7 @@ to provide the `find-revision' operation instead."
 \f
 
 ;; These things should probably be generally available
-
-(defun vc-string-prefix-p (prefix string)
-  (let ((lpref (length prefix)))
-    (and (>= (length string) lpref)
-        (eq t (compare-strings prefix nil nil string nil lpref)))))
+(define-obsolete-function-alias 'vc-string-prefix-p 'string-prefix-p "24.2")
 
 (defun vc-file-tree-walk (dirname func &rest args)
   "Walk recursively through DIRNAME.