]> code.delx.au - gnu-emacs/blobdiff - lisp/vc-dispatcher.el
(pop-up-frame-function): Remove choice nil since it
[gnu-emacs] / lisp / vc-dispatcher.el
index 0fc1c0636d57e37514f6403774a930a797cf34b4..c51a4896b7649b5d08d12fa5d77afe85b8d40543 100644 (file)
@@ -1,6 +1,6 @@
 ;;; vc-dispatcher.el -- generic command-dispatcher facility.
 
-;; Copyright (C) 2008
+;; Copyright (C) 2008, 2009
 ;;   Free Software Foundation, Inc.
 
 ;; Author:     FSF (see below for full credits)
@@ -9,10 +9,10 @@
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Credits:
 
 ;; Designed and implemented by Eric S. Raymond, originally as part of VC mode.
+;; Stefan Monnier and Dan Nicolaescu contributed substantial work on the
+;; vc-dir front end.
 
 ;;; Commentary:
 
 ;; to address that involves selecting sets of files, or possibly
 ;; directories, and passing the selection set to slave commands.  The
 ;; prototypical example, from which this code is derived, is talking
-;; to version-control systems. 
+;; to version-control systems.
 ;;
 ;; vc-dispatcher.el is written to decouple the UI issues in such front
-;; ends from their application-specific logic. It also provides a
+;; ends from their application-specific logic.  It also provides a
 ;; service layer for running the slave commands either synchronously
 ;; or asynchronously and managing the message/error logs from the
 ;; command runs.
 ;; Dispatcher's universe:
 ;;
 ;; The universe consists of the file tree rooted at the current
-;; directory. The dispatcher's upper layer deduces some subset 
-;; of the file tree from the state of the currently visited buffer 
+;; directory.  The dispatcher's upper layer deduces some subset
+;; of the file tree from the state of the currently visited buffer
 ;; and returns that subset, presumably to a client mode.
 ;;
-;; The user may be attempting to select one of three contexts: an
-;; explicitly selected fileset, the current working directory, or a
-;; global (null) context.  The user may be looking at either of two
-;; different views; a buffer visiting a file, or a directory buffer
-;; generated by vc-dispatcher.  The main UI problem connected with
-;; this mode is that the user may need to be able to select any of
-;; these three contexts from either view.
+;; The user may be looking at either of two different views; a buffer
+;; visiting a file, or a directory buffer generated by vc-dispatcher.
 ;;
 ;; The lower layer of this mode runs commands in subprocesses, either
 ;; synchronously or asynchronously.  Commands may be launched in one
 ;; of two ways: they may be run immediately, or the calling mode can
 ;; create a closure associated with a text-entry buffer, to be
-;; executed when the user types C-c to ship the buffer contents. In
+;; executed when the user types C-c to ship the buffer contents.  In
 ;; either case the command messages and error (if any) will remain
 ;; available in a status buffer.
 
-(provide 'vc-dispatcher)
+;; Special behavior of dispatcher directory buffers:
+;;
+;; In dispatcher directory buffers, facilities to perform basic
+;; navigation and selection operations are provided by keymap and menu
+;; entries that dispatcher sets up itself, so they'll be uniform
+;; across all dispatcher-using client modes.  Client modes are
+;; expected to append to these to provide mode-specific bindings.
+;;
+;; The standard map associates a 'state' slot (that the client mode
+;; may set) with each directory entry.  The dispatcher knows nothing
+;; about the semantics of individual states, but mark and unmark commands
+;; treat all entries with the same state as the currently selected one as
+;; a unit.
 
-;; General customization
+;; The interface:
+;;
+;; The main interface to the lower level is vc-do-command.  This launches a
+;; command, synchronously or asynchronously, making the output available
+;; in a command log buffer.  Two other functions, (vc-start-annotation) and
+;; (vc-finish-logentry), allow you to associate a command closure with an
+;; annotation buffer so that when the user confirms the comment the closure
+;; is run (with the comment as part of its context).
+;;
+;; The interface to the upper level has the two main entry points (vc-dir)
+;; and (vc-dispatcher-selection-set) and a couple of convenience functions.
+;; (vc-dir) sets up a dispatcher browsing buffer; (vc-dispatcher-selection-set)
+;; returns a selection set of files, either the marked files in a browsing
+;; buffer or the singleton set consisting of the file visited by the current
+;; buffer (when that is appropriate).  It also does what is needed to ensure
+;; that on-disk files and the contents of their visiting Emacs buffers
+;; coincide.
+;;
+;; When the client mode adds a local mode-line-hook to a buffer, it
+;; will be called with the buffer file name as argument whenever the
+;; dispatcher resynchs the buffer.
 
+;; To do:
+;;
+;; - log buffers need font-locking.
+;;
+
+;; General customization
 (defcustom vc-logentry-check-hook nil
   "Normal hook run by `vc-finish-logentry'.
 Use this to impose your own rules on the entry in addition to any the
-version control backend imposes itself."
+dispatcher client mode imposes itself."
   :type 'hook
   :group 'vc)
 
 (defcustom vc-delete-logbuf-window t
-  "If non-nil, delete the *VC-log* buffer and window after each logical action.
+  "If non-nil, delete the log buffer and window after each logical action.
 If nil, bury that buffer instead.
 This is most useful if you have multiple windows on a frame and would like to
 preserve the setting."
@@ -98,6 +131,11 @@ preserve the setting."
   :type 'boolean
   :group 'vc)
 
+(defcustom vc-suppress-confirm nil
+  "If non-nil, treat user as expert; suppress yes-no prompts on some things."
+  :type 'boolean
+  :group 'vc)
+
 ;; Variables the user doesn't need to know about.
 
 (defvar vc-log-operation nil)
@@ -107,7 +145,7 @@ preserve the setting."
 
 ;; In a log entry buffer, this is a local variable
 ;; that points to the buffer for which it was made
-;; (either a file, or a VC dired buffer).
+;; (either a file, or a directory buffer).
 (defvar vc-parent-buffer nil)
 (put 'vc-parent-buffer 'permanent-local t)
 (defvar vc-parent-buffer-name nil)
@@ -184,7 +222,7 @@ Another is that undo information is not kept."
        (concat " " (propertize "[waiting...]"
                                 'face 'mode-line-emphasis
                                 'help-echo
-                                "A VC command is in progress in this buffer"))))
+                                "A command is in progress in this buffer"))))
 
 (defun vc-exec-after (code)
   "Eval CODE when the current buffer's process is done.
@@ -231,16 +269,17 @@ and is passed 3 arguments: the COMMAND, the FILES and the FLAGS.")
 
 ;;;###autoload
 (defun vc-do-command (buffer okstatus command file-or-list &rest flags)
-  "Execute a VC command, notifying user and checking for errors.
-Output from COMMAND goes to BUFFER, or *vc* if BUFFER is nil or the
-current buffer if BUFFER is t.  If the destination buffer is not
-already current, set it up properly and erase it.  The command is
-considered successful if its exit status does not exceed OKSTATUS (if
-OKSTATUS is nil, that means to ignore error status, if it is `async', that
-means not to wait for termination of the subprocess; if it is t it means to
-ignore all execution errors).  FILE-OR-LIST is the name of a working file;
-it may be a list of files or be nil (to execute commands that don't expect
-a file name or set of files).  If an optional list of FLAGS is present,
+  "Execute a slave command, notifying user and checking for errors.
+Output from COMMAND goes to BUFFER, or the current buffer if
+BUFFER is t.  If the destination buffer is not already current,
+set it up properly and erase it.  The command is considered
+successful if its exit status does not exceed OKSTATUS (if
+OKSTATUS is nil, that means to ignore error status, if it is
+`async', that means not to wait for termination of the
+subprocess; if it is t it means to ignore all execution errors).
+FILE-OR-LIST is the name of a working file; it may be a list of
+files or be nil (to execute commands that don't expect a file
+name or set of files).  If an optional list of FLAGS is present,
 that is inserted into the command line before the filename."
   ;; FIXME: file-relative-name can return a bogus result because
   ;; it doesn't look at the actual file-system to see if symlinks
@@ -250,7 +289,7 @@ that is inserted into the command line before the filename."
                  (if (listp file-or-list) file-or-list (list file-or-list))))
         (full-command
          ;; What we're doing here is preparing a version of the command
-         ;; for display in a debug-progess message.  If it's fewer than
+         ;; for display in a debug-progress message.  If it's fewer than
          ;; 20 characters display the entire command (without trailing
          ;; newline).  Otherwise display the first 20 followed by an ellipsis.
          (concat (if (string= (substring command -1) "\n")
@@ -264,7 +303,7 @@ that is inserted into the command line before the filename."
                  (and (stringp buffer)
                       (string= (buffer-name) buffer))
                  (eq buffer (current-buffer)))
-       (vc-setup-buffer (or buffer "*vc*")))
+       (vc-setup-buffer buffer))
       ;; If there's some previous async process still running, just kill it.
       (let ((oldproc (get-buffer-process (current-buffer))))
         ;; If we wanted to wait for oldproc to finish before doing
@@ -285,9 +324,6 @@ that is inserted into the command line before the filename."
                             (mapconcat 'identity vc-path path-separator))
                     process-environment))
              (w32-quote-process-args t))
-         (when (and (eq okstatus 'async) (file-remote-p default-directory))
-           ;; start-process does not support remote execution
-           (setq okstatus nil))
          (if (eq okstatus 'async)
              ;; Run asynchronously.
              (let ((proc
@@ -301,7 +337,7 @@ that is inserted into the command line before the filename."
                (vc-exec-after
                 `(if vc-command-messages
                      (message "Running %s in background... done" ',full-command))))
-           ;; Run synchrously
+           ;; Run synchronously
            (when vc-command-messages
              (message "Running %s in foreground..." full-command))
            (let ((buffer-undo-list t))
@@ -316,7 +352,7 @@ that is inserted into the command line before the filename."
              (error "Running %s...FAILED (%s)" full-command
                     (if (integerp status) (format "status %d" status) status))))
          ;; We're done.  But don't emit a status message if running
-         ;; asychronously, it would just mislead.
+         ;; asynchronously, it would just mislead.
          (if (and vc-command-messages (not (eq okstatus 'async)))
              (message "Running %s...OK = %d" full-command status)))
        (vc-exec-after
@@ -325,7 +361,7 @@ that is inserted into the command line before the filename."
        status))))
 
 ;; These functions are used to ensure that the view the user sees is up to date
-;; even if the dispatcher client mode has messed with file contents (as in, 
+;; even if the dispatcher client mode has messed with file contents (as in,
 ;; for example, VCS keyword expansion).
 
 (declare-function view-mode-exit "view" (&optional return-to-alist exit-action all-win))
@@ -375,66 +411,17 @@ If CONTEXT cannot be found, return nil."
 Used by `vc-restore-buffer-context' to later restore the context."
   (let ((point-context (vc-position-context (point)))
        ;; Use mark-marker to avoid confusion in transient-mark-mode.
-       (mark-context  (when (eq (marker-buffer (mark-marker)) (current-buffer))
+       (mark-context (when (eq (marker-buffer (mark-marker)) (current-buffer))
                         (vc-position-context (mark-marker))))
        ;; Make the right thing happen in transient-mark-mode.
-       (mark-active nil)
-       ;; The new compilation code does not use compilation-error-list any
-       ;; more, so the code below is now ineffective and might as well
-       ;; be disabled.  -- Stef
-       ;; ;; We may want to reparse the compilation buffer after revert
-       ;; (reparse (and (boundp 'compilation-error-list) ;compile loaded
-       ;;            ;; Construct a list; each elt is nil or a buffer
-       ;;            ;; if that buffer is a compilation output buffer
-       ;;            ;; that contains markers into the current buffer.
-       ;;            (save-current-buffer
-       ;;              (mapcar (lambda (buffer)
-       ;;                        (set-buffer buffer)
-       ;;                        (let ((errors (or
-       ;;                                       compilation-old-error-list
-       ;;                                       compilation-error-list))
-       ;;                              (buffer-error-marked-p nil))
-       ;;                          (while (and (consp errors)
-       ;;                                      (not buffer-error-marked-p))
-       ;;                            (and (markerp (cdr (car errors)))
-       ;;                                 (eq buffer
-       ;;                                     (marker-buffer
-       ;;                                      (cdr (car errors))))
-       ;;                                 (setq buffer-error-marked-p t))
-       ;;                            (setq errors (cdr errors)))
-       ;;                          (if buffer-error-marked-p buffer)))
-       ;;                      (buffer-list)))))
-       (reparse nil))
-    (list point-context mark-context reparse)))
+       (mark-active nil))
+    (list point-context mark-context nil)))
 
 (defun vc-restore-buffer-context (context)
   "Restore point/mark, and reparse any affected compilation buffers.
 CONTEXT is that which `vc-buffer-context' returns."
   (let ((point-context (nth 0 context))
-       (mark-context (nth 1 context))
-       ;; (reparse (nth 2 context))
-        )
-    ;; The new compilation code does not use compilation-error-list any
-    ;; more, so the code below is now ineffective and might as well
-    ;; be disabled.  -- Stef
-    ;; ;; Reparse affected compilation buffers.
-    ;; (while reparse
-    ;;   (if (car reparse)
-    ;;           (with-current-buffer (car reparse)
-    ;;             (let ((compilation-last-buffer (current-buffer)) ;select buffer
-    ;;                   ;; Record the position in the compilation buffer of
-    ;;                   ;; the last error next-error went to.
-    ;;                   (error-pos (marker-position
-    ;;                               (car (car-safe compilation-error-list)))))
-    ;;               ;; Reparse the error messages as far as they were parsed before.
-    ;;               (compile-reinitialize-errors '(4) compilation-parsing-end)
-    ;;               ;; Move the pointer up to find the error we were at before
-    ;;               ;; reparsing.  Now next-error should properly go to the next one.
-    ;;               (while (and compilation-error-list
-    ;;                           (/= error-pos (car (car compilation-error-list))))
-    ;;                 (setq compilation-error-list (cdr compilation-error-list))))))
-    ;;   (setq reparse (cdr reparse)))
-
+       (mark-context (nth 1 context)))
     ;; if necessary, restore point and mark
     (if (not (vc-context-matches-p (point) point-context))
        (let ((new-point (vc-find-position-by-context point-context)))
@@ -486,30 +473,64 @@ editing!"
                     (and (not view-mode)
                          (not (eq (get major-mode 'mode-class) 'special))
                          (view-mode-enter))))
-            ;; FIXME: Call into vc.el
-            (vc-mode-line buffer-file-name))
+            (run-hook-with-args 'mode-line-hook buffer-file-name))
         (kill-buffer (current-buffer)))))
 
+(declare-function vc-dir-resynch-file "vc-dir" (&optional fname))
+(declare-function vc-string-prefix-p "vc" (prefix string))
+
+(defun vc-resynch-buffers-in-directory (directory &optional keep noquery)
+  "Resync all buffers that visit files in DIRECTORY."
+  (dolist (buffer (buffer-list))
+    (let ((fname (buffer-file-name buffer)))
+      (when (and fname (vc-string-prefix-p directory fname))
+       (vc-resynch-buffer fname keep noquery)))))
+
 (defun vc-resynch-buffer (file &optional keep noquery)
   "If FILE is currently visited, resynch its buffer."
   (if (string= buffer-file-name file)
       (vc-resynch-window file keep noquery)
-    (let ((buffer (get-file-buffer file)))
-      (when buffer
-       (with-current-buffer buffer
-         (vc-resynch-window file keep noquery)))))
-  ;; FIME: Call into vc.el
-  (vc-directory-resynch-file file)
-  (when (memq 'vc-dir-mark-buffer-changed after-save-hook)
-    (let ((buffer (get-file-buffer file)))
-      ;; FIME: Call into vc.el
-      (vc-dir-mark-buffer-changed file))))
+    (if (file-directory-p file)
+       (vc-resynch-buffers-in-directory file keep noquery)
+      (let ((buffer (get-file-buffer file)))
+       (when buffer
+         (with-current-buffer buffer
+           (vc-resynch-window file keep noquery))))))
+  ;; Try to avoid unnecessary work, a *vc-dir* buffer is only present
+  ;; if this is true.
+  (when (memq 'vc-dir-resynch-file after-save-hook)
+    (vc-dir-resynch-file file)))
+
+(defun vc-buffer-sync (&optional not-urgent)
+  "Make sure the current buffer and its working file are in sync.
+NOT-URGENT means it is ok to continue if the user says not to save."
+  (when (buffer-modified-p)
+    (if (or vc-suppress-confirm
+           (y-or-n-p (format "Buffer %s modified; save it? " (buffer-name))))
+       (save-buffer)
+      (unless not-urgent
+       (error "Aborted")))))
 
 ;; Command closures
 
-(defun vc-start-logentry (files extra comment initial-contents msg action &optional after-hook)
+;; Set up key bindings for use while editing log messages
+
+(defun vc-log-edit (fileset)
+  "Set up `log-edit' for use on FILE."
+  (setq default-directory
+       (with-current-buffer vc-parent-buffer default-directory))
+  (log-edit 'vc-finish-logentry
+           nil
+           `((log-edit-listfun . (lambda () ',fileset))
+             (log-edit-diff-function . (lambda () (vc-diff nil)))))
+  (set (make-local-variable 'vc-log-fileset) fileset)
+  (make-local-variable 'vc-log-extra)
+  (set-buffer-modified-p nil)
+  (setq buffer-file-name nil))
+
+(defun vc-start-logentry (files extra comment initial-contents msg logbuf action &optional after-hook)
   "Accept a comment for an operation on FILES with extra data EXTRA.
-If COMMENT is nil, pop up a VC-log buffer, emit MSG, and set the
+If COMMENT is nil, pop up a LOGBUF 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
@@ -519,16 +540,16 @@ 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-after-operation-hook'."
   (let ((parent
-         (if (or (eq major-mode 'vc-dired-mode) (eq major-mode 'vc-dir-mode))
-             ;; If we are called from VC dired, the parent buffer is
+         (if (vc-dispatcher-browsing)
+             ;; If we are called from a directory browser, the parent buffer is
              ;; the current buffer.
              (current-buffer)
            (if (and files (equal (length files) 1))
                (get-file-buffer (car files))
              (current-buffer)))))
     (if (and comment (not initial-contents))
-       (set-buffer (get-buffer-create "*VC-log*"))
-      (pop-to-buffer (get-buffer-create "*VC-log*")))
+       (set-buffer (get-buffer-create logbuf))
+      (pop-to-buffer (get-buffer-create logbuf)))
     (set (make-local-variable 'vc-parent-buffer) parent)
     (set (make-local-variable 'vc-parent-buffer-name)
         (concat " from " (buffer-name vc-parent-buffer)))
@@ -545,6 +566,8 @@ for `vc-log-after-operation-hook'."
        (message "%s  Type C-c C-c when done" msg)
       (vc-finish-logentry (eq comment t)))))
 
+(declare-function vc-dir-move-to-goal-column "vc-dir" ())
+
 (defun vc-finish-logentry (&optional nocomment)
   "Complete the operation implied by the current log entry.
 Use the contents of the current buffer as a check-in or registration
@@ -555,13 +578,14 @@ the buffer contents as a comment."
   (unless nocomment
     (run-hooks 'vc-logentry-check-hook))
   ;; Sync parent buffer in case the user modified it while editing the comment.
-  ;; But not if it is a vc-dired buffer.
+  ;; But not if it is a vc-dir buffer.
   (with-current-buffer vc-parent-buffer
-    (or vc-dired-mode (eq major-mode 'vc-dir-mode) (vc-buffer-sync)))
+    (or (vc-dispatcher-browsing) (vc-buffer-sync)))
   (unless vc-log-operation
     (error "No log operation is pending"))
   ;; save the parameters held in buffer-local variables
-  (let ((log-operation vc-log-operation)
+  (let ((logbuf (current-buffer))
+       (log-operation vc-log-operation)
        (log-fileset vc-log-fileset)
        (log-extra vc-log-extra)
        (log-entry (buffer-string))
@@ -575,26 +599,82 @@ the buffer contents as a comment."
               log-extra
               log-entry))
     ;; Remove checkin window (after the checkin so that if that fails
-    ;; we don't zap the *VC-log* buffer and the typing therein).
+    ;; we don't zap the log buffer and the typing therein).
     ;; -- IMO this should be replaced with quit-window
-    (let ((logbuf (get-buffer "*VC-log*")))
-      (cond ((and logbuf vc-delete-logbuf-window)
-            (delete-windows-on logbuf (selected-frame))
-            ;; Kill buffer and delete any other dedicated windows/frames.
-            (kill-buffer logbuf))
-           (logbuf (pop-to-buffer "*VC-log*")
-                   (bury-buffer)
-                   (pop-to-buffer tmp-vc-parent-buffer))))
+    (cond ((and logbuf vc-delete-logbuf-window)
+          (delete-windows-on logbuf (selected-frame))
+          ;; Kill buffer and delete any other dedicated windows/frames.
+          (kill-buffer logbuf))
+         (logbuf (pop-to-buffer logbuf)
+                 (bury-buffer)
+                 (pop-to-buffer tmp-vc-parent-buffer)))
     ;; Now make sure we see the expanded headers
     (when log-fileset
       (mapc
        (lambda (file) (vc-resynch-buffer file vc-keep-workfiles t))
        log-fileset))
-    ;; FIXME: Call into vc.el
-    (when vc-dired-mode
-      (dired-move-to-filename))
-    (when (eq major-mode 'vc-dir-mode)
+    (when (vc-dispatcher-browsing)
       (vc-dir-move-to-goal-column))
     (run-hooks after-hook 'vc-finish-logentry-hook)))
 
+(defun vc-dispatcher-browsing ()
+  "Are we in a directory browser buffer?"
+  (derived-mode-p 'vc-dir-mode))
+
+;; These are unused.
+;; (defun vc-dispatcher-in-fileset-p (fileset)
+;;   (let ((member nil))
+;;     (while (and (not member) fileset)
+;;       (let ((elem (pop fileset)))
+;;         (if (if (file-directory-p elem)
+;;                 (eq t (compare-strings buffer-file-name nil (length elem)
+;;                                        elem nil nil))
+;;               (eq (current-buffer) (get-file-buffer elem)))
+;;             (setq member t))))
+;;     member))
+
+;; (defun vc-dispatcher-selection-set (&optional observer)
+;;   "Deduce a set of files to which to apply an operation.  Return a cons
+;; cell (SELECTION . FILESET), where SELECTION is what the user chose
+;; and FILES is the flist with any directories replaced by the listed files
+;; within them.
+
+;; If we're in a directory display, the fileset is the list of marked files (if
+;; there is one) else the file on the current line.  If not in a directory
+;; display, but the current buffer visits a file, the fileset is a singleton
+;; containing that file.  Otherwise, throw an error."
+;;   (let ((selection
+;;          (cond
+;;           ;; Browsing with vc-dir
+;;           ((vc-dispatcher-browsing)
+;;        ;; If no files are marked, temporarily mark current file
+;;        ;; and choose on that basis (so we get subordinate files)
+;;        (if (not (vc-dir-marked-files))
+;;              (prog2
+;;                (vc-dir-mark-file)
+;;                (cons (vc-dir-marked-files) (vc-dir-marked-only-files))
+;;                (vc-dir-unmark-all-files t))
+;;          (cons (vc-dir-marked-files) (vc-dir-marked-only-files))))
+;;           ;; Visiting an eligible file
+;;           ((buffer-file-name)
+;;            (cons (list buffer-file-name) (list buffer-file-name)))
+;;           ;; No eligible file -- if there's a parent buffer, deduce from there
+;;           ((and vc-parent-buffer (or (buffer-file-name vc-parent-buffer)
+;;                                      (with-current-buffer vc-parent-buffer
+;;                                        (vc-dispatcher-browsing))))
+;;            (with-current-buffer vc-parent-buffer
+;;              (vc-dispatcher-selection-set)))
+;;           ;; No good set here, throw error
+;;           (t (error "No fileset is available here")))))
+;;     ;; We assume, in order to avoid unpleasant surprises to the user,
+;;     ;; that a fileset is not in good shape to be handed to the user if the
+;;     ;; buffers visiting the fileset don't match the on-disk contents.
+;;     (unless observer
+;;       (save-some-buffers
+;;        nil (lambda () (vc-dispatcher-in-fileset-p (cdr selection)))))
+;;     selection))
+
+(provide 'vc-dispatcher)
+
+;; arch-tag: 7d08b17f-5470-4799-914b-bfb9fcf6a246
 ;;; vc-dispatcher.el ends here