;;; vc.el --- drive a version-control system from within Emacs
-;; Copyright (C) 1992-1998, 2000-2011 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
: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
(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
(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))))
(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
;;;###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))
;; 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"))
(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))
(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))
;; 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).
(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.
;; 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)))
(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))))))
;;;###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.
(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))
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)
(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)
;; 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
;;;###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.
(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)
(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)
\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.