diff options
Diffstat (limited to 'lisp/vc/vc-svn.el')
| -rw-r--r-- | lisp/vc/vc-svn.el | 209 |
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. |
