summaryrefslogtreecommitdiff
path: root/lisp/vc/vc-svn.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/vc/vc-svn.el')
-rw-r--r--lisp/vc/vc-svn.el209
1 files changed, 115 insertions, 94 deletions
diff --git a/lisp/vc/vc-svn.el b/lisp/vc/vc-svn.el
index 0e020614fd2..4ef63a23db5 100644
--- a/lisp/vc/vc-svn.el
+++ b/lisp/vc/vc-svn.el
@@ -1,6 +1,6 @@
-;;; 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>
@@ -83,7 +83,7 @@ If t, use no switches."
t ;`svn' doesn't support common args like -c or -b.
"String or list of strings specifying extra switches for svn diff under VC.
If nil, use the value of `vc-diff-switches' (or `diff-switches'),
-together with \"-x --diff-cmd=\"`diff-command' (since 'svn diff'
+together with \"-x --diff-cmd=\"`diff-command' (since `svn diff'
does not support the default \"-c\" value of `diff-switches').
If you want to force an empty list of arguments, use t."
:type '(choice (const :tag "Unspecified" nil)
@@ -95,7 +95,18 @@ If you want to force an empty list of arguments, use t."
:version "22.1"
:group 'vc-svn)
-(defcustom vc-svn-header '("\$Id\$")
+(defcustom vc-svn-annotate-switches nil
+ "String or list of strings specifying switches for svn annotate under VC.
+If nil, use the value of `vc-annotate-switches'. If t, use no
+switches."
+ :type '(choice (const :tag "Unspecified" nil)
+ (const :tag "None" t)
+ (string :tag "Argument String")
+ (repeat :tag "Argument List" :value ("") string))
+ :version "25.1"
+ :group 'vc-svn)
+
+(defcustom vc-svn-header '("$Id\ $")
"Header keywords to be inserted by `vc-insert-headers'."
:version "24.1" ; no longer consult the obsolete vc-header-alist
:type '(repeat string)
@@ -115,7 +126,7 @@ If you want to force an empty list of arguments, use t."
;;; 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
@@ -135,6 +146,7 @@ If you want to force an empty list of arguments, use t."
(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))
@@ -153,36 +165,17 @@ If you want to force an empty list of arguments, use t."
(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)
@@ -195,7 +188,7 @@ If you want to force an empty list of arguments, use t."
(?~ . 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)
@@ -215,31 +208,21 @@ If you want to force an empty list of arguments, use 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"))
@@ -268,7 +251,7 @@ RESULT is a list of conses (FILE . STATE) for directory DIR."
;; 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))))
@@ -293,12 +276,28 @@ RESULT is a list of conses (FILE . STATE) for directory DIR."
(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")))
+ (let ((defdir (expand-file-name default-directory))
+ (svn-prog (executable-find "svn")))
+ (when (and (fboundp 'w32-application-type)
+ (eq (w32-application-type svn-prog) 'msys))
+ (setq defdir
+ (replace-regexp-in-string "^\\(.\\):/" "/\\1/"
+ defdir)))
+ (concat (if (and (stringp defdir)
+ (eq (aref defdir 0) ?/))
+ "file://"
+ ;; MS-Windows files d:/foo/bar need to
+ ;; begin with 3 leading slashes.
+ "file:///")
+ defdir
+ "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'
@@ -310,13 +309,8 @@ to the SVN command."
(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)))))
@@ -354,22 +348,34 @@ This is only possible if SVN is responsible for FILE's directory.")
(defun vc-svn-ignore (file &optional directory remove)
"Ignore FILE under Subversion.
-If DIRECTORY is non-nil, the repository to use will be deduced by
-DIRECTORY; if REMOVE is non-nil, remove FILE from ignored files."
- (vc-svn-command t 0 file "propedit" "svn:ignore"))
+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-ignore-completion-table (file)
- "Return the list of ignored files."
- )
+(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 editable rev)
+(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
@@ -394,6 +400,29 @@ DIRECTORY; if REMOVE is non-nil, remove FILE from ignored files."
(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."
@@ -458,7 +487,7 @@ 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.
@@ -510,7 +539,7 @@ or svn+ssh://."
(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.
@@ -526,7 +555,6 @@ If LIMIT is non-nil, show no more than this many entries."
'vc-svn-command
buffer
'async
- ;; (if (and (= (length files) 1) (vc-stay-local-p file 'SVN)) 'async 0)
(list file)
"log"
(append
@@ -546,7 +574,7 @@ If LIMIT is non-nil, show no more than this many entries."
(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)
@@ -561,14 +589,12 @@ If LIMIT is non-nil, show no more than this many entries."
;; 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"
@@ -594,7 +620,7 @@ NAME is assumed to be a URL."
(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.
@@ -610,7 +636,7 @@ NAME is assumed to be a URL."
;; 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."
@@ -633,17 +659,6 @@ and that it passes `vc-svn-global-switches' to it before FLAGS."
(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
@@ -675,19 +690,23 @@ and that it passes `vc-svn-global-switches' to it before FLAGS."
(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 ??)
@@ -742,7 +761,9 @@ information about FILENAME and return its status."
;; Support for `svn annotate'
(defun vc-svn-annotate-command (file buf &optional rev)
- (vc-svn-command buf 'async file "annotate" (if rev (concat "-r" rev))))
+ (apply #'vc-svn-command buf 'async file "annotate"
+ (append (vc-switches 'svn 'annotate)
+ (if rev (list (concat "-r" rev))))))
(defun vc-svn-annotate-time-of-rev (rev)
;; Arbitrarily assume 10 commits per day.