-;;; vc-svn.el --- non-resident support for Subversion version-control
+;;; vc-svn.el --- non-resident support for Subversion version-control -*- lexical-binding:t -*-
-;; Copyright (C) 2003-2013 Free Software Foundation, Inc.
+;; Copyright (C) 2003-2015 Free Software Foundation, Inc.
;; Author: FSF (see vc.el for full credits)
;; Maintainer: Stefan Monnier <monnier@gnu.org>
;;; Properties of the backend
(defun vc-svn-revision-granularity () 'repository)
-(defun vc-svn-checkout-model (files) 'implicit)
+(defun vc-svn-checkout-model (_files) 'implicit)
;;;
;;; State-querying functions
(defun vc-svn-registered (file)
"Check if FILE is SVN registered."
+ (setq file (expand-file-name file))
(when (vc-svn-root file)
(with-temp-buffer
(cd (file-name-directory file))
(let ((parsed (vc-svn-parse-status file)))
(and parsed (not (memq parsed '(ignored unregistered))))))))))
-(defun vc-svn-state (file &optional localp)
+(defun vc-svn-state (file)
"SVN-specific version of `vc-state'."
(let (process-file-side-effects)
- (setq localp (or localp (vc-stay-local-p file 'SVN)))
(with-temp-buffer
(cd (file-name-directory file))
- (vc-svn-command t 0 file "status" (if localp "-v" "-u"))
+ (vc-svn-command t 0 file "status" "-v")
(vc-svn-parse-status file))))
-;; NB this does not handle svn properties, which can be changed
-;; without changing the file timestamp.
-;; Note that unlike vc-cvs-state-heuristic, this is not called from
-;; vc-svn-state. AFAICS, it is only called from vc-state-refresh via
-;; vc-after-save (bug#7850). Therefore the fact that it ignores
-;; properties is irrelevant. If you want to make vc-svn-state call
-;; this, it should be extended to handle svn properties.
-(defun vc-svn-state-heuristic (file)
- "SVN-specific state heuristic."
- ;; If the file has not changed since checkout, consider it `up-to-date'.
- ;; Otherwise consider it `edited'. Copied from vc-cvs-state-heuristic.
- (let ((checkout-time (vc-file-getprop file 'vc-checkout-time))
- (lastmod (nth 5 (file-attributes file))))
- (cond
- ((equal checkout-time lastmod) 'up-to-date)
- ((string= (vc-working-revision file) "0") 'added)
- ((null checkout-time) 'unregistered)
- (t 'edited))))
-
;; FIXME it would be better not to have the "remote" argument,
;; but to distinguish the two output formats based on content.
+;; FIXME: the local format isn't used by the (sole) caller anymore.
(defun vc-svn-after-dir-status (callback &optional remote)
(let ((state-map '((?A . added)
(?C . conflict)
(?~ . edited)))
(re (if remote "^\\(.\\)\\(.\\).....? \\([ *]\\) +\\(?:[-0-9]+\\)? \\(.*\\)$"
;; Subexp 3 is a dummy in this case, so the numbers match.
- "^\\(.\\)\\(.\\)...\\(.\\) \\(.*\\)$"))
+ "^\\(.\\)\\(.\\)...\\(.\\).? \\(.*\\)$"))
result)
(goto-char (point-min))
(while (re-search-forward re nil t)
(setq result (cons (list filename state) result)))))
(funcall callback result)))
-;; -dir-status called from vc-dir, which loads vc, which loads vc-dispatcher.
+;; dir-status-files called from vc-dir, which loads vc,
+;; which loads vc-dispatcher.
(declare-function vc-exec-after "vc-dispatcher" (code))
-(defun vc-svn-dir-status (dir callback)
+(autoload 'vc-expand-dirs "vc")
+
+(defun vc-svn-dir-status-files (_dir files callback)
"Run 'svn status' for DIR and update BUFFER via CALLBACK.
CALLBACK is called as (CALLBACK RESULT BUFFER), where
RESULT is a list of conses (FILE . STATE) for directory DIR."
- ;; FIXME should this rather be all the files in dir?
- ;; FIXME: the vc-stay-local-p logic below is disabled, it ends up
- ;; calling synchronously (vc-svn-registered DIR) => calling svn status -v DIR
- ;; which is VERY SLOW for big trees and it makes emacs
- ;; completely unresponsive during that time.
- (let* ((local (and nil (vc-stay-local-p dir 'SVN)))
- (remote (or t (not local) (eq local 'only-file))))
- (vc-svn-command (current-buffer) 'async nil "status"
- (if remote "-u"))
- (vc-exec-after
- `(vc-svn-after-dir-status (quote ,callback) ,remote))))
-
-(defun vc-svn-dir-status-files (dir files default-state callback)
- (apply 'vc-svn-command (current-buffer) 'async nil "status" files)
- (vc-exec-after
- `(vc-svn-after-dir-status (quote ,callback))))
-
-(defun vc-svn-dir-extra-headers (dir)
+ ;; FIXME shouldn't this rather default to all the files in dir?
+ (apply #'vc-svn-command (current-buffer) 'async nil "status" "-u" files)
+ (vc-run-delayed (vc-svn-after-dir-status callback t)))
+
+(defun vc-svn-dir-extra-headers (_dir)
"Generate extra status headers for a Subversion working copy."
(let (process-file-side-effects)
(vc-svn-command "*vc*" 0 nil "info"))
;; vc-svn-mode-line-string doesn't exist because the default implementation
;; works just fine.
-(defun vc-svn-previous-revision (file rev)
+(defun vc-svn-previous-revision (_file rev)
(let ((newrev (1- (string-to-number rev))))
(when (< 0 newrev)
(number-to-string newrev))))
(defun vc-svn-create-repo ()
"Create a new SVN repository."
(vc-do-command "*vc*" 0 "svnadmin" '("create" "SVN"))
+ ;; Expand default-directory because svn gets confused by eg
+ ;; file://~/path/to/file. (Bug#15446).
(vc-svn-command "*vc*" 0 "." "checkout"
- (concat "file://" default-directory "SVN")))
+ (concat "file://" (expand-file-name default-directory) "SVN")))
(autoload 'vc-switches "vc")
-(defun vc-svn-register (files &optional rev comment)
+(defun vc-svn-register (files &optional _comment)
"Register FILES into the SVN version-control system.
The COMMENT argument is ignored This does an add but not a commit.
Passes either `vc-svn-register-switches' or `vc-register-switches'
(defalias 'vc-svn-responsible-p 'vc-svn-root)
-(defalias 'vc-svn-could-register 'vc-svn-root
- "Return non-nil if FILE could be registered in SVN.
-This is only possible if SVN is responsible for FILE's directory.")
-
-(defun vc-svn-checkin (files rev comment &optional extra-args-ignored)
+(defun vc-svn-checkin (files comment &optional _extra-args-ignored)
"SVN-specific version of `vc-backend-checkin'."
- (if rev (error "Committing to a specific revision is unsupported in SVN"))
(let ((status (apply
'vc-svn-command nil 1 files "ci"
(nconc (list "-m" comment) (vc-switches 'SVN 'checkin)))))
(concat "-r" rev))
(vc-switches 'SVN 'checkout))))
-(defun vc-svn-checkout (file &optional editable rev)
+(defun vc-svn-ignore (file &optional directory remove)
+ "Ignore FILE under Subversion.
+FILE is a file wildcard, relative to the root directory of DIRECTORY."
+ (let* ((ignores (vc-svn-ignore-completion-table directory))
+ (file (file-relative-name file directory))
+ (ignores (if remove
+ (delete file ignores)
+ (push file ignores))))
+ (vc-svn-command nil 0 nil nil "propset" "svn:ignore"
+ (mapconcat #'identity ignores "\n")
+ (expand-file-name directory))))
+
+(defun vc-svn-ignore-completion-table (directory)
+ "Return the list of ignored files in DIRECTORY."
+ (with-temp-buffer
+ (vc-svn-command t t nil "propget" "svn:ignore" (expand-file-name directory))
+ (split-string (buffer-string))))
+
+(defun vc-svn-find-admin-dir (file)
+ "Return the administrative directory of FILE."
+ (expand-file-name vc-svn-admin-directory (vc-svn-root file)))
+
+(defun vc-svn-checkout (file &optional rev)
(message "Checking out %s..." file)
(with-current-buffer (or (get-file-buffer file) (current-buffer))
- (vc-svn-update file editable rev (vc-switches 'SVN 'checkout)))
+ (vc-svn-update file rev (vc-switches 'SVN 'checkout)))
(vc-mode-line file 'SVN)
(message "Checking out %s...done" file))
-(defun vc-svn-update (file editable rev switches)
+(defun vc-svn-update (file rev switches)
(if (and (file-exists-p file) (not rev))
;; If no revision was specified, there's nothing to do.
nil
(unless contents-done
(vc-svn-command nil 0 file "revert")))
+(defun vc-svn-merge-file (file)
+ "Accept a file merge request, prompting for revisions."
+ (let* ((first-revision
+ (vc-read-revision
+ (concat "Merge " file
+ " from SVN revision "
+ "(default news on current branch): ")
+ (list file)
+ 'SVN))
+ second-revision
+ status)
+ (cond
+ ((string= first-revision "")
+ (setq status (vc-svn-merge-news file)))
+ (t
+ (setq second-revision
+ (vc-read-revision
+ "Second SVN revision: "
+ (list file) 'SVN nil
+ first-revision))
+ (setq status (vc-svn-merge file first-revision second-revision))))
+ status))
+
(defun vc-svn-merge (file first-version &optional second-version)
"Merge changes into current working copy of FILE.
The changes are between FIRST-VERSION and SECOND-VERSION."
(error "Couldn't analyze svn update result")))
(message "Merging changes into %s...done" file))))
-(defun vc-svn-modify-change-comment (files rev comment)
+(defun vc-svn-modify-change-comment (_files rev comment)
"Modify the change comments for a specified REV.
You must have ssh access to the repository host, and the directory Emacs
uses locally for temp files must also be writable by you on that host.
(autoload 'vc-setup-buffer "vc-dispatcher")
-(defun vc-svn-print-log (files buffer &optional shortlog start-revision limit)
+(defun vc-svn-print-log (files buffer &optional _shortlog start-revision limit)
"Print commit log associated with FILES into specified BUFFER.
SHORTLOG is ignored.
If START-REVISION is non-nil, it is the newest revision to show.
'vc-svn-command
buffer
'async
- ;; (if (and (= (length files) 1) (vc-stay-local-p file 'SVN)) 'async 0)
(list file)
"log"
(append
(if start-revision (format "-r%s" start-revision) "-rHEAD:0"))
(when limit (list "--limit" (format "%s" limit)))))))))
-(defun vc-svn-diff (files &optional oldvers newvers buffer)
+(defun vc-svn-diff (files &optional oldvers newvers buffer async)
"Get a difference report using SVN between two revisions of fileset FILES."
(and oldvers
(not newvers)
;; has a different revision, we fetch the lot, which is
;; obviously sub-optimal.
(setq oldvers nil))
+ (setq async (and async (or oldvers newvers))) ; Svn diffs those locally.
(let* ((switches
(if vc-svn-diff-switches
(vc-switches 'SVN 'diff)
(list (concat "--diff-cmd=" diff-command) "-x"
- (mapconcat 'identity (vc-switches nil 'diff) " "))))
- (async (and (not vc-disable-async-diff)
- (vc-stay-local-p files 'SVN)
- (or oldvers newvers)))) ; Svn diffs those locally.
+ (mapconcat 'identity (vc-switches nil 'diff) " ")))))
(apply 'vc-svn-command buffer
(if async 'async 0)
files "diff"
(vc-svn-command nil 0 dir "copy" name)
(when branchp (vc-svn-retrieve-tag dir name nil)))
-(defun vc-svn-retrieve-tag (dir name update)
+(defun vc-svn-retrieve-tag (dir name _update)
"Retrieve a tag at and below DIR.
NAME is the name of the tag; if it is empty, do a `svn update'.
If UPDATE is non-nil, then update (resynch) any affected buffers.
;; Subversion makes backups for us, so don't bother.
;; (defun vc-svn-make-version-backups-p (file)
;; "Return non-nil if version backups should be made for FILE."
-;; (vc-stay-local-p file 'SVN))
+;; nil)
(defun vc-svn-check-headers ()
"Check if the current file has any headers in it."
(cons vc-svn-global-switches flags)
(append vc-svn-global-switches flags))))
-(defun vc-svn-repository-hostname (dirname)
- (with-temp-buffer
- (let (process-file-side-effects)
- (vc-svn-command t t dirname "info" "--xml"))
- (goto-char (point-min))
- (when (re-search-forward "<url>\\(.*\\)</url>" nil t)
- ;; This is not a hostname but a URL. This may actually be considered
- ;; as a feature since it allows vc-svn-stay-local to specify different
- ;; behavior for different modules on the same server.
- (match-string 1))))
-
(defun vc-svn-resolve-when-done ()
"Call \"svn resolved\" if the conflict markers have been removed."
(save-excursion
(defun vc-svn-parse-status (&optional filename)
"Parse output of \"svn status\" command in the current buffer.
-Set file properties accordingly. Unless FILENAME is non-nil, parse only
-information about FILENAME and return its status."
- (let (file status propstat)
+Set file properties accordingly. If FILENAME is non-nil, return its status."
+ (let (multifile file status propstat)
(goto-char (point-min))
(while (re-search-forward
;; Ignore the files with status X.
"^\\(?:\\?\\|[ ACDGIMR!~][ MC][ L][ +][ S]..\\([ *]\\) +\\([-0-9]+\\) +\\([0-9?]+\\) +\\([^ ]+\\)\\) +" nil t)
;; If the username contains spaces, the output format is ambiguous,
;; so don't trust the output's filename unless we have to.
- (setq file (or filename
+ (setq file (or (unless multifile filename)
(expand-file-name
- (buffer-substring (point) (line-end-position)))))
- (setq status (char-after (line-beginning-position))
+ (buffer-substring (point) (line-end-position))))
+ ;; If we are parsing the result of running status on a directory,
+ ;; there could be multiple files in the output.
+ ;; We assume that filename, if supplied, applies to the first
+ ;; listed file (ie, the directory). Bug#15322.
+ multifile t
+ status (char-after (line-beginning-position))
;; Status of the item's properties ([ MC]).
propstat (char-after (1+ (line-beginning-position))))
(if (eq status ??)