;; Author: FSF (see below for full credits)
;; Maintainer: Andre Spiegel <spiegel@gnu.org>
+;; $Id: vc.el,v 1.284 2000/10/26 20:53:11 monnier Exp $
+
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
;; 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>
;; Only required if files can be locked by somebody else.
;; * register (file rev comment)
;; * unregister (file backend)
-;; - receive-file (file move)
+;; - receive-file (file rev)
;; - responsible-p (file)
;; Should also work if FILE is a directory (ends with a slash).
;; - could-register (file)
;; * print-log (file)
;; Insert the revision log of FILE into the current buffer.
;; - show-log-entry (version)
+;; - wash-log (file)
+;; Remove all non-comment information from the output of print-log
;; - comment-history (file)
;; - update-changelog (files)
;; Find changelog entries for FILES, or for all files at or below
(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
(save-excursion
,@body)
(vc-checkin file nil ,comment)))
+(put 'with-vc-file 'indent-function 1)
;;;###autoload
(defmacro edit-vc-file (file comment &rest body)
(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."
(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))))
(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-backend file))
+ (if (not (vc-registered file))
(vc-register verbose comment)
(vc-recompute-state file)
(if visited (vc-mode-line file))
(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
(set-file-modes buffer-file-name
(logior (file-modes buffer-file-name) 128))
(toggle-read-only -1))
-
+
;; edited
((eq state 'edited)
(cond
(if (member vsym vc-handled-backends)
(vc-transfer-file file vsym)
(vc-checkin file version comment)))))))
-
+
;; locked by somebody else
((stringp state)
(if comment
(if verbose (read-string "Version to steal: ")
(vc-workfile-version file))
state))
-
+
;; needs-patch
((eq state 'needs-patch)
(if (yes-or-no-p (format
(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
(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))
(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: "
"Enter initial comment."
(lambda (file rev comment)
(message "Registering %s... " file)
- (let ((backend (vc-find-new-backend file)))
+ (let ((backend (vc-responsible-backend file t)))
(vc-file-clearprops file)
(vc-call-backend backend 'register file rev comment)
(vc-file-setprop file 'vc-backend backend)
(setq backup-inhibited t)))
(message "Registering %s... done" file))))
-(defun vc-responsible-backend (file &optional backends)
- "Return the name of the backend system that is responsible for FILE.
-If no backend in variable `vc-handled-backends' declares itself
-responsible, the first backend in that list will be returned.
-FILE can also be a directory name (ending with a slash).
-If BACKENDS is non-nil it overrides any current backend or
-`vc-handled-backends'."
- (or (and (not backends) (not (file-directory-p file)) (vc-backend file))
- (progn
- (unless backends (setq backends vc-handled-backends))
- (unless backends (error "No reponsible backend"))
- (catch 'found
- (dolist (backend backends)
- (if (vc-call-backend backend 'responsible-p file)
- (throw 'found backend)))
- (car backends)))))
-
-(defun vc-find-new-backend (file)
- "Find a new backend to register FILE."
- (let (backends)
- ;; We can't register if it's already registered
- (dolist (backend vc-handled-backends)
- (when (and (not (vc-call-backend backend 'registered file))
- (vc-call-backend backend 'could-register file))
- (push backend backends)))
- (unless backends
- (error "Cannot register, no appropriate backend in `vc-handled-backends'"))
- (vc-responsible-backend file (nreverse backends))))
+
+(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
+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
+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, 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
+be registered."
+ (if (not vc-handled-backends)
+ (error "No handled backends"))
+ (or (and (not (file-directory-p file)) (not register) (vc-backend file))
+ (catch 'found
+ ;; First try: find a responsible backend. If this is for registration,
+ ;; it must be a backend under which FILE is not yet registered.
+ (dolist (backend vc-handled-backends)
+ (and (or (not register)
+ (not (vc-call-backend backend 'registered file)))
+ (vc-call-backend backend 'responsible-p file)
+ (throw 'found backend)))
+ ;; no responsible backend
+ (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")))))
(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)
The default implementation returns t for all files."
t)
-(defun vc-unregister (file)
- "Unregister FILE from version control system BACKEND."
- (vc-call unregister file)
- (vc-file-clearprops file))
-
-(defun vc-default-unregister (backend file)
- "Default implementation of `vc-unregister', signals an error."
- (error "Unregistering files is not supported for %s" backend))
-
(defun vc-resynch-window (file &optional keep noquery)
"If FILE is in the current buffer, either revert or unvisit it.
The choice between revert (to see expanded keywords) and unvisit depends on
(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
(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.
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-p file)
+ (vc-up-to-date-p file)
+ (vc-make-version-backup file))
(with-vc-properties
file
(condition-case err
(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)
(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))
;; 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))
- (vc-call checkin file rev comment))
- `((vc-state up-to-date)
- (vc-checkout-time ,(nth 5 (file-attributes file)))
- (vc-workfile-version nil)))
+ (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)))
(message "Checking in %s...done" file))
'vc-checkin-hook))
(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))
(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)))
(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))
rel2-default ") ")
"Newer version (default: current source): ")
nil nil rel2-default))))
- (if (string-equal rel1 "") (setq rel1 nil))
- (if (string-equal rel2 "") (setq rel2 nil))
(vc-setup-buffer "*vc-diff*")
(if (file-directory-p file)
+ ;; recursive directory diff
(let ((inhibit-read-only t))
+ (if (string-equal rel1 "") (setq rel1 nil))
+ (if (string-equal rel2 "") (setq rel2 nil))
(insert "Diffs between "
(or rel1 "last version checked in")
" and "
(vc-call-backend ',(vc-backend file) 'diff ',f ',rel1 ',rel2)))))
(vc-exec-after `(let ((inhibit-read-only t))
(insert "\nEnd of diffs.\n"))))
-
- (cd (file-name-directory file))
- (vc-call diff file rel1 rel2))
+ ;; single file diff
+ (if (or (not rel1) (string-equal rel1 ""))
+ (setq rel1 (vc-workfile-version file)))
+ (if (string-equal rel2 "")
+ (setq rel2 nil))
+ (let ((file-rel1 (vc-version-backup-file file rel1))
+ (file-rel2 (if (not rel2)
+ file
+ (vc-version-backup-file file rel2))))
+ (if (and 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))
(not (get-buffer-process (current-buffer))))
(progn
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
"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 "")
(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.
(let ((file buffer-file-name)
;; This operation should always ask for confirmation.
(vc-suppress-confirm nil)
- (obuf (current-buffer)))
+ (obuf (current-buffer))
+ status)
(unless (vc-workfile-unchanged-p file)
- (vc-diff nil t)
- (vc-exec-after `(message nil))
- (unwind-protect
- (if (not (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))))
+ ;; 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"))
+ (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)
- (with-vc-properties
- file
- (vc-call revert file)
- `((vc-state up-to-date)
- (vc-checkout-time ,(nth 5 (file-attributes file)))))
- (vc-resynch-buffer file t t)
+ (vc-revert-file file)
(message "Reverting %s...done" file)))
+(defun vc-version-backup-file (file &optional rev)
+ "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-p file)
+ (let ((backup-file (vc-version-backup-file-name file rev)))
+ (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."
+ (with-vc-properties
+ file
+ (let ((backup-file (vc-version-backup-file file)))
+ (if (not backup-file)
+ (vc-call revert file)
+ (copy-file backup-file file 'ok-if-already-exists 'keep-date)
+ (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
(defun vc-cancel-version (norevert)
"Get rid of most recently checked in version of this file.
(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
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
backend, then commit all changes that were made under the current
backend to NEW-BACKEND, and unregister FILE from the current backend.
\(If FILE is not yet registered under NEW-BACKEND, register it.)"
- (let ((old-backend (vc-backend file)))
+ (let* ((old-backend (vc-backend file))
+ (edited (memq (vc-state file) '(edited needs-merge)))
+ (registered (vc-call-backend new-backend 'registered file))
+ (move
+ (and registered ; Never move if not registered in new-backend yet.
+ ;; move if new-backend comes later in vc-handled-backends
+ (or (memq new-backend (memq old-backend vc-handled-backends))
+ (y-or-n-p "Final transfer? "))))
+ (comment nil))
(if (eq old-backend new-backend)
- (error "%s is the current backend of %s"
- new-backend file)
- (with-vc-properties
- file
- (vc-call-backend new-backend 'receive-file file
- ;; set MOVE argument if new-backend
- ;; comes later in vc-handled-backends
- (memq new-backend
- (memq old-backend vc-handled-backends)))
- `((vc-backend ,new-backend))))
- (vc-resynch-buffer file t t)))
-
-(defun vc-default-receive-file (backend file move)
- "Let BACKEND receive FILE from another version control system.
-If MOVE is non-nil, then FILE is unregistered from the old
-backend and its comment history is used as the initial contents
-of the log entry buffer."
- (let ((old-backend (vc-backend file))
- (rev (vc-workfile-version file))
- (state (vc-state file))
- (comment (and move (vc-call comment-history file))))
- (if move (vc-unregister file))
- (vc-file-clearprops file)
- (if (not (vc-call-backend backend 'registered file))
- (with-vc-properties
- file
- ;; TODO: If the file was 'edited under the old backend,
- ;; this should actually register the version
- ;; it was based on.
- (vc-call-backend backend 'register file rev "")
- `((vc-backend ,backend)))
- (vc-file-setprop file 'vc-backend backend)
- (vc-file-setprop file 'vc-state 'edited)
- (set-file-modes file
- (logior (file-modes file) 128)))
- (when (or move (eq state 'edited))
+ (error "%s is the current backend of %s" new-backend file))
+ (if registered
+ (set-file-modes file (logior (file-modes file) 128))
+ ;; `registered' might have switched under us.
+ (vc-switch-backend file old-backend)
+ (let* ((rev (vc-workfile-version file))
+ (modified-file (and edited (make-temp-name file)))
+ (unmodified-file (and modified-file (vc-version-backup-file file))))
+ ;; Go back to the base unmodified file.
+ (unwind-protect
+ (progn
+ (when modified-file
+ (copy-file file modified-file)
+ ;; If we have a local copy of the unmodified file, handle that
+ ;; here and not in vc-revert-file because we don't want to
+ ;; delete that copy -- it is still useful for OLD-BACKEND.
+ (if unmodified-file
+ (copy-file unmodified-file file 'ok-if-already-exists)
+ (if (y-or-n-p "Get base version from master? ")
+ (vc-revert-file file))))
+ (vc-call-backend new-backend 'receive-file file rev))
+ (when modified-file
+ (vc-switch-backend file new-backend)
+ (unless (eq (vc-checkout-model file) 'implicit)
+ (vc-checkout file t nil))
+ (rename-file modified-file file 'ok-if-already-exists)
+ (vc-file-setprop file 'vc-checkout-time nil)))))
+ (when move
+ (vc-switch-backend file old-backend)
+ (setq comment (vc-call comment-history file))
+ (vc-call unregister file))
+ (vc-switch-backend file new-backend)
+ (when (or move edited)
(vc-file-setprop file 'vc-state 'edited)
+ (vc-mode-line file)
(vc-checkin file nil comment (stringp comment)))))
+(defun vc-default-unregister (backend file)
+ "Default implementation of `vc-unregister', signals an error."
+ (error "Unregistering files is not supported for %s" backend))
+
+(defun vc-default-receive-file (backend file rev)
+ "Let BACKEND receive FILE from another version control system."
+ (vc-call-backend backend 'register file rev ""))
+
(defun vc-rename-master (oldmaster newfile templates)
"Rename OLDMASTER to be the master file for NEWFILE based on TEMPLATES."
(let* ((dir (file-name-directory (expand-file-name oldmaster)))
(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)))