]> code.delx.au - gnu-emacs/blobdiff - lisp/vc/vc-svn.el
Merge from origin/emacs-24
[gnu-emacs] / lisp / vc / vc-svn.el
index 39b107b81b569f7370a22c9b527f7b7d11d81e34..d74daf9c36fac3fb2f7d30373fa87a5bfe479304 100644 (file)
@@ -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>
@@ -115,7 +115,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 +135,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 +154,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 +177,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 +197,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 +240,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 +265,14 @@ 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")))
+                  (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'
@@ -310,13 +284,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)))))
@@ -352,14 +321,36 @@ This is only possible if SVN is responsible for FILE's directory.")
                (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
@@ -384,6 +375,29 @@ This is only possible if SVN is responsible for FILE's directory.")
   (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."
@@ -448,7 +462,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.
@@ -500,7 +514,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.
@@ -516,7 +530,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
@@ -536,7 +549,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)
@@ -551,14 +564,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"
@@ -584,7 +595,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.
@@ -600,7 +611,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."
@@ -623,17 +634,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
@@ -665,19 +665,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 ??)