]> code.delx.au - gnu-emacs/blobdiff - lisp/vc-svn.el
(vc-svn-diff): Revert previous change but add a test
[gnu-emacs] / lisp / vc-svn.el
index 0b0dd1ed74cb110603d9ad7bf83bf900ffbe3e3b..e79ef9a0fa6ee671a0b7e8cbe35e9060a901b9b2 100644 (file)
@@ -1,16 +1,17 @@
 ;;; vc-svn.el --- non-resident support for Subversion version-control
 
-;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009
+;;   Free Software Foundation, Inc.
 
 ;; Author:      FSF (see vc.el for full credits)
 ;; Maintainer:  Stefan Monnier <monnier@gnu.org>
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
-;; This is preliminary support for Subversion (http://subversion.tigris.org/).
-;; It started as `sed s/cvs/svn/ vc.cvs.el' (from version 1.56)
-;; and hasn't been completely fixed since.
-
-;; Sync'd with Subversion's vc-svn.el as of revision 5801.
-
-;;; Bugs:
-
-;; - VC-dired is (really) slow.
+;; Sync'd with Subversion's vc-svn.el as of revision 5801. but this version
+;; has been extensively modified since to handle filesets.
 
 ;;; Code:
 
 ;;; Customization options
 ;;;
 
+;; FIXME there is also svnadmin.
+(defcustom vc-svn-program "svn"
+  "Name of the SVN executable."
+  :type 'string
+  :group 'vc)
+
 (defcustom vc-svn-global-switches nil
-  "*Global switches to pass to any SVN command."
+  "Global switches to pass to any SVN command."
   :type '(choice (const :tag "None" nil)
                 (string :tag "Argument String")
                 (repeat :tag "Argument List"
   :group 'vc)
 
 (defcustom vc-svn-register-switches nil
-  "*Extra switches for registering a file into SVN.
+  "Switches for registering a file into SVN.
 A string or list of strings passed to the checkin program by
-\\[vc-register]."
-  :type '(choice (const :tag "None" nil)
+\\[vc-register].  If nil, use the value of `vc-register-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))
+                (repeat :tag "Argument List" :value ("") string))
   :version "22.1"
   :group 'vc)
 
 (defcustom vc-svn-diff-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'.
-If you want to force an empty list of arguments, use t."
+If nil, use the value of `vc-diff-switches' (or `diff-switches'),
+together with \"-x --diff-cmd=diff\" (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)
                 (const :tag "None" t)
                 (string :tag "Argument String")
@@ -80,7 +80,7 @@ If you want to force an empty list of arguments, use t."
   :group 'vc)
 
 (defcustom vc-svn-header (or (cdr (assoc 'SVN vc-header-alist)) '("\$Id\$"))
-  "*Header keywords to be inserted by `vc-insert-headers'."
+  "Header keywords to be inserted by `vc-insert-headers'."
   :version "22.1"
   :type '(repeat string)
   :group 'vc)
@@ -96,6 +96,11 @@ If you want to force an empty list of arguments, use t."
        (t ".svn"))
   "The name of the \".svn\" subdirectory or its equivalent.")
 
+;;; Properties of the backend
+
+(defun vc-svn-revision-granularity () 'repository)
+(defun vc-svn-checkout-model (files) 'implicit)
+
 ;;;
 ;;; State-querying functions
 ;;;
@@ -114,9 +119,6 @@ If you want to force an empty list of arguments, use t."
 ;;;###autoload       (load "vc-svn")
 ;;;###autoload       (vc-svn-registered f))))
 
-;;;###autoload
-(add-to-list 'completion-ignored-extensions ".svn/")
-
 (defun vc-svn-registered (file)
   "Check if FILE is SVN registered."
   (when (file-readable-p (expand-file-name (concat vc-svn-admin-directory
@@ -135,7 +137,8 @@ If you want to force an empty list of arguments, use t."
                ;; an `error' by vc-do-command.
                (error nil))))
         (when (eq 0 status)
-          (vc-svn-parse-status file))))))
+         (let ((parsed (vc-svn-parse-status file)))
+           (and parsed (not (memq parsed '(ignored unregistered))))))))))
 
 (defun vc-svn-state (file &optional localp)
   "SVN-specific version of `vc-state'."
@@ -149,55 +152,97 @@ If you want to force an empty list of arguments, use t."
   "SVN-specific state heuristic."
   (vc-svn-state file 'local))
 
-(defun vc-svn-dir-state (dir &optional localp)
-  "Find the SVN state of all files in DIR."
-  (setq localp (or localp (vc-stay-local-p dir)))
-  (let ((default-directory dir))
-    ;; Don't specify DIR in this command, the default-directory is
-    ;; enough.  Otherwise it might fail with remote repositories.
-    (with-temp-buffer
-      (vc-svn-command t 0 nil "status" (if localp "-v" "-u"))
-      (vc-svn-parse-status))))
-
-(defun vc-svn-workfile-version (file)
-  "SVN-specific version of `vc-workfile-version'."
+;; FIXME it would be better not to have the "remote" argument,
+;; but to distinguish the two output formats based on content.
+(defun vc-svn-after-dir-status (callback &optional remote)
+  (let ((state-map '((?A . added)
+                     (?C . conflict)
+                     (?D . removed)
+                     (?I . ignored)
+                     (?M . edited)
+                     (?R . removed)
+                     (?? . unregistered)
+                     ;; This is what vc-svn-parse-status does.
+                     (?~ . edited)))
+       (re (if remote "^\\(.\\)..... \\([ *]\\) +[-0-9]+ +\\(.*\\)$"
+             ;; Subexp 2 is a dummy in this case, so the numbers match.
+             "^\\(.\\)....\\(.\\) \\(.*\\)$"))
+       result)
+    (goto-char (point-min))
+    (while (re-search-forward re nil t)
+      (let ((state (cdr (assq (aref (match-string 1) 0) state-map)))
+           (filename (match-string 3)))
+       (and remote (string-equal (match-string 2) "*")
+            ;; FIXME are there other possible combinations?
+            (cond ((eq state 'edited) (setq state 'needs-merge))
+                  ((not state) (setq state 'needs-update))))
+       (when state
+         (setq result (cons (list filename state) result)))))
+    (funcall callback result)))
+
+(defun vc-svn-dir-status (dir 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)))
+        (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)
+  "Generate extra status headers for a Subversion working copy."
+  (vc-svn-command "*vc*" 0 nil "info")
+  (let ((repo
+        (save-excursion
+          (and (progn
+                 (set-buffer "*vc*")
+                 (goto-char (point-min))
+                 (re-search-forward "Repository Root: *\\(.*\\)" nil t))
+               (match-string 1)))))
+    (concat
+     (cond (repo
+           (concat
+            (propertize "Repository : " 'face 'font-lock-type-face)
+            (propertize repo 'face 'font-lock-variable-name-face)))
+          (t "")))))
+
+(defun vc-svn-working-revision (file)
+  "SVN-specific version of `vc-working-revision'."
   ;; There is no need to consult RCS headers under SVN, because we
   ;; get the workfile version for free when we recognize that a file
   ;; is registered in SVN.
   (vc-svn-registered file)
-  (vc-file-getprop file 'vc-workfile-version))
-
-(defun vc-svn-checkout-model (file)
-  "SVN-specific version of `vc-checkout-model'."
-  ;; It looks like Subversion has no equivalent of CVSREAD.
-  'implicit)
+  (vc-file-getprop file 'vc-working-revision))
 
 ;; vc-svn-mode-line-string doesn't exist because the default implementation
 ;; works just fine.
 
-(defun vc-svn-dired-state-info (file)
-  "SVN-specific version of `vc-dired-state-info'."
-  (let ((svn-state (vc-state file)))
-    (cond ((eq svn-state 'edited)
-          (if (equal (vc-workfile-version file) "0")
-              "(added)" "(modified)"))
-         ((eq svn-state 'needs-patch) "(patch)")
-         ((eq svn-state 'needs-merge) "(merge)"))))
-
-(defun vc-svn-previous-version (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-next-version (file rev)
+(defun vc-svn-next-revision (file rev)
   (let ((newrev (1+ (string-to-number rev))))
-    ;; The "workfile version" is an uneasy conceptual fit under Subversion;
+    ;; The "working revision" is an uneasy conceptual fit under Subversion;
     ;; we use it as the upper bound until a better idea comes along.  If the
     ;; workfile version W coincides with the tree's latest revision R, then
     ;; this check prevents a "no such revision: R+1" error.  Otherwise, it
     ;; inhibits showing of W+1 through R, which could be considered anywhere
     ;; from gracious to impolite.
-    (unless (< (string-to-number (vc-file-getprop file 'vc-workfile-version))
+    (unless (< (string-to-number (vc-file-getprop file 'vc-working-revision))
                newrev)
       (number-to-string newrev))))
 
@@ -206,13 +251,18 @@ If you want to force an empty list of arguments, use t."
 ;;; State-changing functions
 ;;;
 
-(defun vc-svn-register (file &optional rev comment)
-  "Register FILE into the SVN version-control system.
-COMMENT can be used to provide an initial description of FILE.
+(defun vc-svn-create-repo ()
+  "Create a new SVN repository."
+  (vc-do-command "*vc*" 0 "svnadmin" '("create" "SVN"))
+  (vc-do-command "*vc*" 0 vc-svn-program '(".")
+                "checkout" (concat "file://" default-directory "SVN")))
 
-`vc-register-switches' and `vc-svn-register-switches' are passed to
-the SVN command (in that order)."
-  (apply 'vc-svn-command nil 0 file "add" (vc-switches 'SVN 'register)))
+(defun vc-svn-register (files &optional rev 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'
+to the SVN command."
+  (apply 'vc-svn-command nil 0 files "add" (vc-switches 'SVN 'register)))
 
 (defun vc-svn-responsible-p (file)
   "Return non-nil if SVN thinks it is responsible for FILE."
@@ -225,10 +275,11 @@ the SVN command (in that order)."
   "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 (file rev comment)
+(defun vc-svn-checkin (files rev comment)
   "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 file "ci"
+                 'vc-svn-command nil 1 files "ci"
                  (nconc (list "-m" comment) (vc-switches 'SVN 'checkin)))))
     (set-buffer "*vc*")
     (goto-char (point-min))
@@ -236,7 +287,8 @@ This is only possible if SVN is responsible for FILE's directory.")
       ;; Check checkin problem.
       (cond
        ((search-forward "Transaction is out of date" nil t)
-        (vc-file-setprop file 'vc-state 'needs-merge)
+        (mapc (lambda (file) (vc-file-setprop file 'vc-state 'needs-merge))
+             files)
         (error (substitute-command-keys
                 (concat "Up-to-date check failed: "
                         "type \\[vc-next-action] to merge in changes"))))
@@ -247,11 +299,12 @@ This is only possible if SVN is responsible for FILE's directory.")
         (error "Check-in failed"))))
     ;; Update file properties
     ;; (vc-file-setprop
-    ;;  file 'vc-workfile-version
+    ;;  file 'vc-working-revision
     ;;  (vc-parse-buffer "^\\(new\\|initial\\) revision: \\([0-9.]+\\)" 2))
     ))
 
-(defun vc-svn-find-version (file rev buffer)
+(defun vc-svn-find-revision (file rev buffer)
+  "SVN-specific retrieval of a specified version into a buffer."
   (apply 'vc-svn-command
         buffer 0 file
         "cat"
@@ -262,7 +315,7 @@ This is only possible if SVN is responsible for FILE's directory.")
 (defun vc-svn-checkout (file &optional editable rev)
   (message "Checking out %s..." file)
   (with-current-buffer (or (get-file-buffer file) (current-buffer))
-    (vc-call update file editable rev (vc-switches 'SVN 'checkout)))
+    (vc-svn-update file editable rev (vc-switches 'SVN 'checkout)))
   (vc-mode-line file)
   (message "Checking out %s...done" file))
 
@@ -271,11 +324,9 @@ This is only possible if SVN is responsible for FILE's directory.")
       ;; If no revision was specified, there's nothing to do.
       nil
     ;; Check out a particular version (or recreate the file).
-    (vc-file-setprop file 'vc-workfile-version nil)
+    (vc-file-setprop file 'vc-working-revision nil)
     (apply 'vc-svn-command nil 0 file
           "update"
-          ;; default for verbose checkout: clear the sticky tag so
-          ;; that the actual update will get the head of the trunk
           (cond
            ((null rev) "-rBASE")
            ((or (eq rev t) (equal rev "")) nil)
@@ -311,18 +362,18 @@ The changes are between FIRST-VERSION and SECOND-VERSION."
 (defun vc-svn-merge-news (file)
   "Merge in any new changes made to FILE."
   (message "Merging changes into %s..." file)
-  ;; (vc-file-setprop file 'vc-workfile-version nil)
+  ;; (vc-file-setprop file 'vc-working-revision nil)
   (vc-file-setprop file 'vc-checkout-time 0)
   (vc-svn-command nil 0 file "update")
   ;; Analyze the merge result reported by SVN, and set
   ;; file properties accordingly.
   (with-current-buffer (get-buffer "*vc*")
     (goto-char (point-min))
-    ;; get new workfile version
+    ;; get new working revision
     (if (re-search-forward
         "^\\(Updated to\\|At\\) revision \\([0-9]+\\)" nil t)
-       (vc-file-setprop file 'vc-workfile-version (match-string 2))
-      (vc-file-setprop file 'vc-workfile-version nil))
+       (vc-file-setprop file 'vc-working-revision (match-string 2))
+      (vc-file-setprop file 'vc-working-revision nil))
     ;; get file status
     (goto-char (point-min))
     (prog1
@@ -357,58 +408,105 @@ 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)
+  "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.
+This is only supported if the repository access method is either file://
+or svn+ssh://."
+  (let (tempfile host remotefile directory fileurl-p)
+    (with-temp-buffer
+      (vc-do-command (current-buffer) 0 vc-svn-program nil "info")
+      (goto-char (point-min))
+      (unless (re-search-forward "Repository Root: \\(file://\\(/.*\\)\\)\\|\\(svn\\+ssh://\\([^/]+\\)\\(/.*\\)\\)" nil t)
+       (error "Repository information is unavailable"))
+      (if (match-string 1)
+         (progn
+           (setq fileurl-p t)
+           (setq directory (match-string 2)))
+       (setq host (match-string 4))
+       (setq directory (match-string 5))
+       (setq remotefile (concat host ":" tempfile))))
+    (with-temp-file (setq tempfile (make-temp-file user-mail-address))
+      (insert comment))
+    (if fileurl-p
+       ;; Repository Root is a local file.
+       (progn
+         (unless (vc-do-command
+                  "*vc*" 0 "svnadmin" nil
+                  "setlog" "--bypass-hooks" directory
+                  "-r" rev (format "%s" tempfile))
+           (error "Log edit failed"))
+         (delete-file tempfile))
+
+      ;; Remote repository, using svn+ssh.
+      (unless (vc-do-command "*vc*" 0 "scp" nil "-q" tempfile remotefile)
+       (error "Copy of comment to %s failed" remotefile))
+      (unless (vc-do-command
+              "*vc*" 0 "ssh" nil "-q" host
+              (format "svnadmin setlog --bypass-hooks %s -r %s %s; rm %s"
+                      directory rev tempfile tempfile))
+       (error "Log edit failed")))))
 
 ;;;
 ;;; History functions
 ;;;
 
-(defun vc-svn-print-log (file &optional buffer)
-  "Get change log associated with FILE."
+(defvar log-view-per-file-logs)
+
+(define-derived-mode vc-svn-log-view-mode log-view-mode "SVN-Log-View"
+  (require 'add-log)
+  (set (make-local-variable 'log-view-per-file-logs) nil))
+
+(defun vc-svn-print-log (files &optional buffer)
+  "Get change log(s) associated with FILES."
   (save-current-buffer
     (vc-setup-buffer buffer)
     (let ((inhibit-read-only t))
       (goto-char (point-min))
-      ;; Add a line to tell log-view-mode what file this is.
-      (insert "Working file: " (file-relative-name file) "\n"))
-    (vc-svn-command
-     buffer
-     (if (and (vc-stay-local-p file) (fboundp 'start-process)) 'async 0)
-     file "log"
-     ;; By default Subversion only shows the log upto the working version,
-     ;; whereas we also want the log of the subsequent commits.  At least
-     ;; that's what the vc-cvs.el code does.
-     "-rHEAD:0")))
-
-(defun vc-svn-diff (file &optional oldvers newvers buffer)
-  "Get a difference report using SVN between two versions of FILE."
-  (unless buffer (setq buffer "*vc-diff*"))
-  (if (and oldvers (equal oldvers (vc-workfile-version file)))
-      ;; Use nil rather than the current revision because svn handles it
-      ;; better (i.e. locally).
-      (setq oldvers nil))
-  (if (string= (vc-workfile-version file) "0")
-      ;; This file is added but not yet committed; there is no master file.
-      (if (or oldvers newvers)
-         (error "No revisions of %s exist" file)
-       ;; We regard this as "changed".
-       ;; Diff it against /dev/null.
-       ;; Note: this is NOT a "svn diff".
-       (apply 'vc-do-command buffer
-              1 "diff" file
-              (append (vc-switches nil 'diff) '("/dev/null")))
-       ;; Even if it's empty, it's locally modified.
-       1)
-    (let* ((switches
+      (if files
+         (dolist (file files)
+                 (insert "Working file: " file "\n")
+                 (vc-svn-command
+                  buffer
+                  'async
+                  ;; (if (and (= (length files) 1) (vc-stay-local-p file)) 'async 0)
+                  (list file)
+                  "log"
+                  ;; By default Subversion only shows the log up to the
+                  ;; working revision, whereas we also want the log of the
+                  ;; subsequent commits.  At least that's what the
+                  ;; vc-cvs.el code does.
+                  "-rHEAD:0"))
+       ;; Dump log for the entire directory.
+       (vc-svn-command buffer 0 nil "log" "-rHEAD:0")))))
+
+(defun vc-svn-diff (files &optional oldvers newvers buffer)
+  "Get a difference report using SVN between two revisions of fileset FILES."
+  (and oldvers
+       (not newvers)
+       files
+       (catch 'no
+        (dolist (f files)
+          (or (equal oldvers (vc-working-revision f))
+              (throw 'no nil)))
+        t)
+       ;; Use nil rather than the current revision because svn handles
+       ;; it better (i.e. locally).  Note that if _any_ of the files
+       ;; has a different revision, we fetch the lot, which is
+       ;; obviously sub-optimal.
+       (setq oldvers nil))
+  (let* ((switches
            (if vc-svn-diff-switches
                (vc-switches 'SVN 'diff)
-             (list "-x" (mapconcat 'identity (vc-switches nil 'diff) " "))))
+             (list "--diff-cmd=diff" "-x"
+                   (mapconcat 'identity (vc-switches nil 'diff) " "))))
           (async (and (not vc-disable-async-diff)
-                       (vc-stay-local-p file)
-                      (or oldvers newvers) ; Svn diffs those locally.
-                      (fboundp 'start-process))))
+                       (vc-stay-local-p files)
+                      (or oldvers newvers)))) ; Svn diffs those locally.
       (apply 'vc-svn-command buffer
             (if async 'async 0)
-            file "diff"
+            files "diff"
             (append
              switches
              (when oldvers
@@ -417,27 +515,23 @@ The changes are between FIRST-VERSION and SECOND-VERSION."
       (if async 1                    ; async diff => pessimistic assumption
        ;; For some reason `svn diff' does not return a useful
        ;; status w.r.t whether the diff was empty or not.
-       (buffer-size (get-buffer buffer))))))
-
-(defun vc-svn-diff-tree (dir &optional rev1 rev2)
-  "Diff all files at and below DIR."
-  (vc-svn-diff (file-name-as-directory dir) rev1 rev2))
+       (buffer-size (get-buffer buffer)))))
 
 ;;;
-;;; Snapshot system
+;;; Tag system
 ;;;
 
-(defun vc-svn-create-snapshot (dir name branchp)
-  "Assign to DIR's current version a given NAME.
+(defun vc-svn-create-tag (dir name branchp)
+  "Assign to DIR's current revision a given NAME.
 If BRANCHP is non-nil, the name is created as a branch (and the current
 workspace is immediately moved to that new branch).
 NAME is assumed to be a URL."
   (vc-svn-command nil 0 dir "copy" name)
-  (when branchp (vc-svn-retrieve-snapshot dir name nil)))
+  (when branchp (vc-svn-retrieve-tag dir name nil)))
 
-(defun vc-svn-retrieve-snapshot (dir name update)
-  "Retrieve a snapshot at and below DIR.
-NAME is the name of the snapshot; if it is empty, do a `svn 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.
 NAME is assumed to be a URL."
   (vc-svn-command nil 0 dir "switch" name)
@@ -464,11 +558,11 @@ NAME is assumed to be a URL."
 ;;; Internal functions
 ;;;
 
-(defun vc-svn-command (buffer okstatus file &rest flags)
+(defun vc-svn-command (buffer okstatus file-or-list &rest flags)
   "A wrapper around `vc-do-command' for use in vc-svn.el.
 The difference to vc-do-command is that this function always invokes `svn',
 and that it passes `vc-svn-global-switches' to it before FLAGS."
-  (apply 'vc-do-command buffer okstatus "svn" file
+  (apply 'vc-do-command (or buffer "*vc*") okstatus vc-svn-program file-or-list
          (if (stringp vc-svn-global-switches)
              (cons vc-svn-global-switches flags)
            (append vc-svn-global-switches
@@ -487,7 +581,9 @@ and that it passes `vc-svn-global-switches' to it before FLAGS."
           ;; Old `svn' used name="svn:this_dir", newer use just name="".
           (concat "name=\"\\(?:svn:this_dir\\)?\"[\n\t ]*"
                   "\\(?:[-a-z]+=\"[^\"]*\"[\n\t ]*\\)*?"
-                  "url=\"\\([^\"]+\\)\"") nil t)
+                  "url=\"\\(?1:[^\"]+\\)\""
+                   ;; Yet newer ones don't use XML any more.
+                   "\\|^\ndir\n[0-9]+\n\\(?1:.*\\)") 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.
@@ -512,7 +608,7 @@ and that it passes `vc-svn-global-switches' to it before FLAGS."
           (re-search-forward "^<<<<<<< " nil t))
         ;; There are conflict markers.
         (progn
-          (smerge-mode 1)
+          (smerge-start-session)
           (add-hook 'after-save-hook 'vc-svn-resolve-when-done nil t))
       ;; There are no conflict markers.  This is problematic: maybe it means
       ;; the conflict has been resolved and we should immediately call "svn
@@ -529,21 +625,19 @@ information about FILENAME and return its status."
   (let (file status)
     (goto-char (point-min))
     (while (re-search-forward
-            ;; Ignore the files with status in [IX?].
-           "^[ ACDGMR!~][ MC][ L][ +][ S]..\\([ *]\\) +\\([-0-9]+\\) +\\([0-9?]+\\) +\\([^ ]+\\) +" nil t)
+            ;; 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
                      (expand-file-name
                       (buffer-substring (point) (line-end-position)))))
       (setq status (char-after (line-beginning-position)))
-      (unless (eq status ??)
-       ;; `vc-BACKEND-registered' must not set vc-backend,
-       ;; which is instead set in vc-registered.
-       (unless filename (vc-file-setprop file 'vc-backend 'SVN))
+      (if (eq status ??)
+         (vc-file-setprop file 'vc-state 'unregistered)
        ;; Use the last-modified revision, so that searching in vc-print-log
        ;; output works.
-       (vc-file-setprop file 'vc-workfile-version (match-string 3))
+       (vc-file-setprop file 'vc-working-revision (match-string 3))
         ;; Remember Svn's own status.
         (vc-file-setprop file 'vc-svn-status status)
        (vc-file-setprop
@@ -551,25 +645,27 @@ information about FILENAME and return its status."
         (cond
          ((eq status ?\ )
           (if (eq (char-after (match-beginning 1)) ?*)
-              'needs-patch
+              'needs-update
              (vc-file-setprop file 'vc-checkout-time
                               (nth 5 (file-attributes file)))
             'up-to-date))
          ((eq status ?A)
           ;; If the file was actually copied, (match-string 2) is "-".
-          (vc-file-setprop file 'vc-workfile-version "0")
+          (vc-file-setprop file 'vc-working-revision "0")
           (vc-file-setprop file 'vc-checkout-time 0)
-          'edited)
-         ((memq status '(?M ?C))
+          'added)
+         ((eq status ?C)
+          (vc-file-setprop file 'vc-state 'conflict))
+         ((eq status '?M)
           (if (eq (char-after (match-beginning 1)) ?*)
               'needs-merge
             'edited))
+         ((eq status ?I)
+          (vc-file-setprop file 'vc-state 'ignored))
+         ((eq status ?R)
+          (vc-file-setprop file 'vc-state 'removed))
          (t 'edited)))))
-    (if filename (vc-file-getprop filename 'vc-state))))
-
-(defun vc-svn-dir-state-heuristic (dir)
-  "Find the SVN state of all files in DIR, using only local information."
-  (vc-svn-dir-state dir 'local))
+    (when filename (vc-file-getprop filename 'vc-state))))
 
 (defun vc-svn-valid-symbolic-tag-name-p (tag)
   "Return non-nil if TAG is a valid symbolic tag name."
@@ -579,8 +675,8 @@ information about FILENAME and return its status."
   (and (string-match "^[a-zA-Z]" tag)
        (not (string-match "[^a-z0-9A-Z-_]" tag))))
 
-(defun vc-svn-valid-version-number-p (tag)
-  "Return non-nil if TAG is a valid version number."
+(defun vc-svn-valid-revision-number-p (tag)
+  "Return non-nil if TAG is a valid revision number."
   (and (string-match "^[0-9]" tag)
        (not (string-match "[^0-9]" tag))))
 
@@ -593,6 +689,8 @@ information about FILENAME and return its status."
   ;; Arbitrarily assume 10 commmits per day.
   (/ (string-to-number rev) 10.0))
 
+(defvar vc-annotate-parent-rev)
+
 (defun vc-svn-annotate-current-time ()
   (vc-svn-annotate-time-of-rev vc-annotate-parent-rev))