;; Arch, Subversion, Bzr, Git, Mercurial, Monotone and SCCS
;; (or its free replacement, CSSC).
;;
-;; Some features will not work with old RCS versions. Where
-;; appropriate, VC finds out which version you have, and allows or
-;; disallows those features (stealing locks, for example, works only
-;; from 5.6.2 onwards).
-;; Even initial checkins will fail if your RCS version is so old that ci
-;; doesn't understand -t-; this has been known to happen to people running
-;; NExTSTEP 3.0.
-;;
-;; You can support the RCS -x option by customizing vc-rcs-master-templates.
-;;
-;; Proper function of the SCCS diff commands requires the shellscript vcdiff
-;; to be installed somewhere on Emacs's path for executables.
-;;
;; If your site uses the ChangeLog convention supported by Emacs, the
;; function `log-edit-comment-to-change-log' could prove a useful checkin hook,
;; although you might prefer to use C-c C-a (i.e. `log-edit-insert-changelog')
;;
;; HISTORY FUNCTIONS
;;
-;; * print-log (files &optional buffer)
+;; * print-log (files &optional buffer shortlog)
;;
;; Insert the revision log for FILES into BUFFER, or the *vc* buffer
;; if BUFFER is nil. (Note: older versions of this function expected
;; only a single file argument.)
+;; If SHORTLOG is true insert a short version of the log.
;;
;; - log-view-mode ()
;;
;; Invoked from a buffer in vc-annotate-mode, return the revision
;; corresponding to the current line, or nil if there is no revision
;; corresponding to the current line.
+;; If the backend supports annotating through copies and renames,
+;; and displays a file name and a revision, then return a cons
+;; (REVISION . FILENAME).
;;
;; TAG SYSTEM
;;
;; `revert' operations itself, without calling the backend system. The
;; default implementation always returns nil.
;;
+;; - root (file)
+;; Return the root of the VC controlled hierarchy for file.
+;;
;; - repository-hostname (dirname)
;;
;; Return the hostname that the backend will have to contact
;;
;;;; Default Behavior:
;;
-;; - do not default to RCS anymore when the current directory is not
-;; controlled by any VCS and the user does C-x v v
-;;
;; - vc-responsible-backend should not return RCS if no backend
;; declares itself responsible.
;;
registration should be found.
If REGISTER is nil, then if FILE is already registered, return the
-backend of FILE. If FILE is not registered, or a directory, then the
+backend of FILE. If FILE is not registered, then the
first backend in `vc-handled-backends' that declares itself
responsible for FILE is returned. If no backend declares itself
responsible, return the first backend.
+If REGISTER is non-nil and FILE is a directory, create a VC
+repository that can be used to register FILE.
+
If REGISTER is non-nil, return the first responsible backend under
which FILE is not yet registered. If there is no such backend, return
the first backend under which FILE is not yet registered, but could
(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
- ;; could register FILE
- (dolist (backend vc-handled-backends)
- (and (not (vc-call-backend backend 'registered file))
- (vc-call-backend backend 'could-register file)
- (throw 'found backend)))
- (error "No backend that could register")))))
+ (if (file-directory-p file)
+ (let* ((possible-backends
+ (let (pos)
+ (dolist (crt vc-handled-backends)
+ (when (vc-find-backend-function crt 'create-repo)
+ (push crt pos)))
+ pos))
+ (bk
+ (intern
+ ;; Read the VC backend from the user, only
+ ;; complete with the backends that have the
+ ;; 'create-repo method.
+ (completing-read
+ (format "%s is not in a version controlled directory.\nUse VC backend: " file)
+ (mapcar 'symbol-name possible-backends) nil t)))
+ (repo-dir
+ (file-name-as-directory
+ (let ((def-dir file))
+ ;; read the directory where to create the
+ ;; repository, make sure it's a parent of
+ ;; file.
+ (read-file-name
+ (format "create %s repository in: " bk)
+ default-directory nil t nil
+ (lambda (arg)
+ (and (file-directory-p arg)
+ (vc-string-prefix-p (expand-file-name arg) def-dir))))))))
+ (let ((default-directory repo-dir))
+ (vc-call-backend bk 'create-repo))
+ (throw 'found bk))
+
+ ;; FIXME: this case does not happen with the current code.
+ ;; Should we keep it?
+ ;;
+ ;; 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))
+ (vc-call-backend backend 'could-register file)
+ (throw 'found backend))))
+ (error "no backend that could register")))))
(defun vc-expand-dirs (file-or-dir-list)
"Expands directories in a file list specification.
(unless (file-directory-p node) (push node flattened)))
(nreverse flattened)))
-(defun vc-derived-from-dir-mode (&optional buffer)
- "Are we in a VC-directory buffer, or do we have one as an ancestor?"
- (let ((buffer (or buffer (current-buffer))))
- (cond ((derived-mode-p 'vc-dir-mode) t)
- (vc-parent-buffer (vc-derived-from-dir-mode vc-parent-buffer))
- (t nil))))
-
(defvar vc-dir-backend)
-;; FIXME: this is not functional, commented out.
-;; (defun vc-deduce-fileset (&optional observer)
-;; "Deduce a set of files and a backend to which to apply an operation and
-;; the common state of the fileset. Return (BACKEND . FILESET)."
-;; (let* ((selection (vc-dispatcher-selection-set observer))
-;; (raw (car selection)) ;; Selection as user made it
-;; (cooked (cdr selection)) ;; Files only
-;; ;; FIXME: Store the backend in a buffer-local variable.
-;; (backend (if (vc-derived-from-dir-mode (current-buffer))
-;; ;; FIXME: this should use vc-dir-backend from
-;; ;; the *vc-dir* buffer.
-;; (vc-responsible-backend default-directory)
-;; (assert (and (= 1 (length raw))
-;; (not (file-directory-p (car raw)))))
-;; (vc-backend (car cooked)))))
-;; (cons backend selection)))
-
(declare-function vc-dir-current-file "vc-dir" ())
(declare-function vc-dir-deduce-fileset "vc-dir" (&optional state-model-only-files))
((and allow-unregistered (not (vc-registered buffer-file-name)))
(if state-model-only-files
(list (vc-responsible-backend
- (file-name-directory (buffer-file-name)))
+ (file-name-directory (buffer-file-name)) t)
(list buffer-file-name)
(list buffer-file-name)
(when state-model-only-files 'unregistered)
nil)
(list (vc-responsible-backend
- (file-name-directory (buffer-file-name)))
+ (file-name-directory (buffer-file-name)) t)
(list buffer-file-name))))
- (t (error "No fileset is available here.")))))
+ (t (error "No fileset is available here")))))
(defun vc-ensure-vc-buffer ()
"Make sure that the current buffer visits a version-controlled file."
;; Do the right thing
(cond
((eq state 'missing)
- (error "Fileset files are missing, so cannot be operated on."))
+ (error "Fileset files are missing, so cannot be operated on"))
((eq state 'ignored)
- (error "Fileset files are ignored by the version-control system."))
+ (error "Fileset files are ignored by the version-control system"))
((or (null state) (eq state 'unregistered))
(vc-register nil vc-fileset))
;; Files are up-to-date, or need a merge and user specified a revision
"Register the current file with a specified back end."
(interactive "SBackend: ")
(when (not (member backend vc-handled-backends))
- (error "Unknown back end."))
+ (error "Unknown back end"))
(let ((vc-handled-backends (list backend)))
(call-interactively 'vc-register)))
(list files rev1 rev2))))
;; All that was just so we could do argument completion!
(when (and (not rev1) rev2)
- (error "Not a valid revision range."))
+ (error "Not a valid revision range"))
;; Yes, it's painful to call (vc-deduce-fileset) again. Alas, the
;; placement rules for (interactive) don't actually leave us a choice.
- (vc-diff-internal t (vc-deduce-fileset) rev1 rev2 (interactive-p)))
-
-;; (defun vc-contains-version-controlled-file (dir)
-;; "Return t if DIR contains a version-controlled file, nil otherwise."
-;; (catch 'found
-;; (mapc (lambda (f) (and (not (file-directory-p f)) (vc-backend f) (throw 'found 't))) (directory-files dir))
-;; nil))
+ (vc-diff-internal t (vc-deduce-fileset t) rev1 rev2
+ (called-interactively-p 'interactive)))
;;;###autoload
(defun vc-diff (historic &optional not-urgent)
(if historic
(call-interactively 'vc-version-diff)
(when buffer-file-name (vc-buffer-sync not-urgent))
- (vc-diff-internal t (vc-deduce-fileset) nil nil (interactive-p))))
+ (vc-diff-internal t (vc-deduce-fileset t) nil nil
+ (called-interactively-p 'interactive))))
+
+;;;###autoload
+(defun vc-root-diff (historic &optional not-urgent)
+ "Display diffs between file revisions.
+Normally this compares the currently selected fileset with their
+working revisions. With a prefix argument HISTORIC, it reads two revision
+designators specifying which revisions to compare.
+
+The optional argument NOT-URGENT non-nil means it is ok to say no to
+saving the buffer."
+ (interactive (list current-prefix-arg t))
+ (if historic
+ ;; FIXME: this does not work right, `vc-version-diff' ends up
+ ;; calling `vc-deduce-fileset' to find the files to diff, and
+ ;; that's not what we want here, we want the diff for the VC root dir.
+ (call-interactively 'vc-version-diff)
+ (when buffer-file-name (vc-buffer-sync not-urgent))
+ (let ((backend
+ (cond ((derived-mode-p 'vc-dir-mode) vc-dir-backend)
+ (vc-mode (vc-backend buffer-file-name))))
+ rootdir working-revision)
+ (unless backend
+ (error "Buffer is not version controlled"))
+ (setq rootdir (vc-call-backend backend 'root default-directory))
+ (setq working-revision (vc-working-revision rootdir))
+ (vc-diff-internal
+ t (list backend (list rootdir) working-revision) nil nil
+ (called-interactively-p 'interactive)))))
;;;###autoload
(defun vc-revision-other-window (rev)
;; Miscellaneous other entry points
+;; FIXME: this should be a defcustom
+;; FIXME: maybe add another choice:
+;; `root-directory' (or somesuch), which would mean show a short log
+;; for the root directory.
+(defvar vc-log-short-style '(directory)
+ "Whether or not to show a short log.
+If it contains `directory' then if the fileset contains a directory show a short log.
+If it contains `file' then show short logs for files.
+Not all VC backends support short logs!")
+
(defun vc-print-log-internal (backend files working-revision)
;; Don't switch to the output buffer before running the command,
;; so that any buffer-local settings in the vc-controlled
;; buffer can be accessed by the command.
- (vc-call-backend backend 'print-log files "*vc-change-log*")
- (pop-to-buffer "*vc-change-log*")
- (vc-exec-after
- `(let ((inhibit-read-only t))
- (vc-call-backend ',backend 'log-view-mode)
- (set (make-local-variable 'log-view-vc-backend) ',backend)
- (set (make-local-variable 'log-view-vc-fileset) ',files)
-
- (shrink-window-if-larger-than-buffer)
- ;; move point to the log entry for the working revision
- (vc-call-backend ',backend 'show-log-entry ',working-revision)
- (setq vc-sentinel-movepoint (point))
- (set-buffer-modified-p nil))))
+ (let ((dir-present nil)
+ (vc-short-log nil))
+ (dolist (file files)
+ (when (file-directory-p file)
+ (setq dir-present t)))
+ (setq vc-short-log
+ (not (null (if dir-present
+ (memq 'directory vc-log-short-style)
+ (memq 'file vc-log-short-style)))))
+ (vc-call-backend backend 'print-log files "*vc-change-log*" vc-short-log)
+ (pop-to-buffer "*vc-change-log*")
+ (vc-exec-after
+ `(let ((inhibit-read-only t)
+ (vc-short-log ,vc-short-log))
+ (vc-call-backend ',backend 'log-view-mode)
+ (set (make-local-variable 'log-view-vc-backend) ',backend)
+ (set (make-local-variable 'log-view-vc-fileset) ',files)
+
+ (shrink-window-if-larger-than-buffer)
+ ;; move point to the log entry for the working revision
+ (vc-call-backend ',backend 'show-log-entry ',working-revision)
+ (setq vc-sentinel-movepoint (point))
+ (set-buffer-modified-p nil)))))
;;;###autoload
(defun vc-print-log (&optional working-revision)
(working-revision (or working-revision (vc-working-revision (car files)))))
(vc-print-log-internal backend files working-revision)))
+;;;###autoload
+(defun vc-print-root-log ()
+ "List the change log of for the current VC controlled tree in a window."
+ (interactive)
+ (let ((backend
+ (cond ((derived-mode-p 'vc-dir-mode) vc-dir-backend)
+ (vc-mode (vc-backend buffer-file-name))))
+ rootdir working-revision)
+ (unless backend
+ (error "Buffer is not version controlled"))
+ (setq rootdir (vc-call-backend backend 'root default-directory))
+ (setq working-revision (vc-working-revision rootdir))
+ (vc-print-log-internal backend (list rootdir) working-revision)))
+
;;;###autoload
(defun vc-revert ()
"Revert working copies of the selected fileset to their repository contents.
(dolist (file files)
(let ((buf (get-file-buffer file)))
(when (and buf (buffer-modified-p buf))
- (error "Please kill or save all modified buffers before reverting.")))
+ (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))
(error "Revert canceled"))))
(error "Rollback requires a singleton fileset or repository versioning"))
;; FIXME: latest-on-branch-p should take the fileset.
(when (not (vc-call-backend backend 'latest-on-branch-p (car files)))
- (error "Rollback is only possible at the tip revision."))
+ (error "Rollback is only possible at the tip revision"))
;; 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
(vc-buffer-sync nil))
(dolist (file files)
(when (buffer-modified-p (get-file-buffer file))
- (error "Please kill or save all modified buffers before rollback."))
+ (error "Please kill or save all modified buffers before rollback"))
(when (not (vc-up-to-date-p file))
- (error "Please revert all modified workfiles before rollback.")))
+ (error "Please revert all modified workfiles before rollback")))
;; Accumulate changes associated with the fileset
(vc-setup-buffer "*vc-diff*")
(not-modified)