authors.el: Add some renamed/moved files
[bpt/emacs.git] / lisp / vc / vc-svn.el
CommitLineData
9c750eba 1;;; vc-svn.el --- non-resident support for Subversion version-control -*- lexical-binding:t -*-
1fd3454a 2
ba318903 3;; Copyright (C) 2003-2014 Free Software Foundation, Inc.
1fd3454a
SM
4
5;; Author: FSF (see vc.el for full credits)
6;; Maintainer: Stefan Monnier <monnier@gnu.org>
bd78fa1d 7;; Package: vc
1fd3454a
SM
8
9;; This file is part of GNU Emacs.
10
eb3fa2cf 11;; GNU Emacs is free software: you can redistribute it and/or modify
1fd3454a 12;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
1fd3454a
SM
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
eb3fa2cf 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
1fd3454a
SM
23
24;;; Commentary:
25
f9914e54
ER
26;; Sync'd with Subversion's vc-svn.el as of revision 5801. but this version
27;; has been extensively modified since to handle filesets.
1fd3454a 28
1fd3454a
SM
29;;; Code:
30
31(eval-when-compile
32 (require 'vc))
33
8228a275
MH
34;; Clear up the cache to force vc-call to check again and discover
35;; new functions when we reload this file.
36(put 'SVN 'vc-functions nil)
37
1fd3454a
SM
38;;;
39;;; Customization options
40;;;
41
67b0de11
CY
42(defgroup vc-svn nil
43 "VC Subversion (svn) backend."
44 :version "24.1"
45 :group 'vc)
46
f9d5dc48
GM
47;; FIXME there is also svnadmin.
48(defcustom vc-svn-program "svn"
49 "Name of the SVN executable."
50 :type 'string
67b0de11 51 :group 'vc-svn)
f9d5dc48 52
31db8c31
GM
53;; Might be nice if svn defaulted to non-interactive if stdin not tty.
54;; http://svn.haxx.se/dev/archive-2008-05/0762.shtml
55;; http://svn.haxx.se/dev/archive-2009-04/0094.shtml
56;; Maybe newer ones do?
57(defcustom vc-svn-global-switches (unless (eq system-type 'darwin) ; bug#13513
58 '("--non-interactive"))
59 "Global switches to pass to any SVN command.
60The option \"--non-interactive\" is often needed to prevent SVN
61hanging while prompting for authorization."
1fd3454a
SM
62 :type '(choice (const :tag "None" nil)
63 (string :tag "Argument String")
64 (repeat :tag "Argument List"
65 :value ("")
66 string))
31db8c31 67 :version "24.4"
67b0de11 68 :group 'vc-svn)
1fd3454a
SM
69
70(defcustom vc-svn-register-switches nil
c8d6b4bc 71 "Switches for registering a file into SVN.
1fd3454a 72A string or list of strings passed to the checkin program by
c8d6b4bc
GM
73\\[vc-register]. If nil, use the value of `vc-register-switches'.
74If t, use no switches."
75 :type '(choice (const :tag "Unspecified" nil)
76 (const :tag "None" t)
1fd3454a 77 (string :tag "Argument String")
c8d6b4bc 78 (repeat :tag "Argument List" :value ("") string))
bf247b6e 79 :version "22.1"
67b0de11 80 :group 'vc-svn)
1fd3454a 81
8462aca5
SM
82(defcustom vc-svn-diff-switches
83 t ;`svn' doesn't support common args like -c or -b.
84 "String or list of strings specifying extra switches for svn diff under VC.
9751169a 85If nil, use the value of `vc-diff-switches' (or `diff-switches'),
ed68f651
SS
86together with \"-x --diff-cmd=\"`diff-command' (since 'svn diff'
87does not support the default \"-c\" value of `diff-switches').
88If you want to force an empty list of arguments, use t."
8462aca5
SM
89 :type '(choice (const :tag "Unspecified" nil)
90 (const :tag "None" t)
1fd3454a
SM
91 (string :tag "Argument String")
92 (repeat :tag "Argument List"
93 :value ("")
94 string))
bf247b6e 95 :version "22.1"
67b0de11 96 :group 'vc-svn)
1fd3454a 97
67141a37 98(defcustom vc-svn-header '("\$Id\$")
f9d5dc48 99 "Header keywords to be inserted by `vc-insert-headers'."
67141a37 100 :version "24.1" ; no longer consult the obsolete vc-header-alist
1fd3454a 101 :type '(repeat string)
67b0de11 102 :group 'vc-svn)
1fd3454a 103
fc2fb30c
SM
104;; We want to autoload it for use by the autoloaded version of
105;; vc-svn-registered, but we want the value to be compiled at startup, not
106;; at dump time.
107;; ;;;###autoload
108(defconst vc-svn-admin-directory
109 (cond ((and (memq system-type '(cygwin windows-nt ms-dos))
ace2fad9
CY
110 (getenv "SVN_ASP_DOT_NET_HACK"))
111 "_svn")
112 (t ".svn"))
113 "The name of the \".svn\" subdirectory or its equivalent.")
114
8cdd17b4
ER
115;;; Properties of the backend
116
70e2f6c7 117(defun vc-svn-revision-granularity () 'repository)
9c750eba 118(defun vc-svn-checkout-model (_files) 'implicit)
70e2f6c7 119
1fd3454a
SM
120;;;
121;;; State-querying functions
122;;;
123
ace2fad9
CY
124;;; vc-svn-admin-directory is generally not defined when the
125;;; autoloaded function is called.
126
1fd3454a 127;;;###autoload (defun vc-svn-registered (f)
ace2fad9 128;;;###autoload (let ((admin-dir (cond ((and (eq system-type 'windows-nt)
fc2fb30c
SM
129;;;###autoload (getenv "SVN_ASP_DOT_NET_HACK"))
130;;;###autoload "_svn")
131;;;###autoload (t ".svn"))))
b5a53795 132;;;###autoload (when (vc-find-root f admin-dir)
af314ba0 133;;;###autoload (load "vc-svn" nil t)
ace2fad9 134;;;###autoload (vc-svn-registered f))))
1fd3454a
SM
135
136(defun vc-svn-registered (file)
137 "Check if FILE is SVN registered."
b5a53795 138 (when (vc-svn-root file)
1fd3454a
SM
139 (with-temp-buffer
140 (cd (file-name-directory file))
04473f67
MA
141 (let* (process-file-side-effects
142 (status
df4da7f4
SM
143 (condition-case nil
144 ;; Ignore all errors.
145 (vc-svn-command t t file "status" "-v")
146 ;; Some problem happened. E.g. We can't find an `svn'
147 ;; executable. We used to only catch `file-error' but when
148 ;; the process is run on a remote host via Tramp, the error
149 ;; is only reported via the exit status which is turned into
150 ;; an `error' by vc-do-command.
151 (error nil))))
152 (when (eq 0 status)
b5e791bd
DN
153 (let ((parsed (vc-svn-parse-status file)))
154 (and parsed (not (memq parsed '(ignored unregistered))))))))))
1fd3454a
SM
155
156(defun vc-svn-state (file &optional localp)
157 "SVN-specific version of `vc-state'."
04473f67
MA
158 (let (process-file-side-effects)
159 (setq localp (or localp (vc-stay-local-p file 'SVN)))
160 (with-temp-buffer
161 (cd (file-name-directory file))
162 (vc-svn-command t 0 file "status" (if localp "-v" "-u"))
163 (vc-svn-parse-status file))))
1fd3454a 164
9749e2b0
GM
165;; NB this does not handle svn properties, which can be changed
166;; without changing the file timestamp.
167;; Note that unlike vc-cvs-state-heuristic, this is not called from
168;; vc-svn-state. AFAICS, it is only called from vc-state-refresh via
169;; vc-after-save (bug#7850). Therefore the fact that it ignores
170;; properties is irrelevant. If you want to make vc-svn-state call
171;; this, it should be extended to handle svn properties.
1fd3454a
SM
172(defun vc-svn-state-heuristic (file)
173 "SVN-specific state heuristic."
9749e2b0
GM
174 ;; If the file has not changed since checkout, consider it `up-to-date'.
175 ;; Otherwise consider it `edited'. Copied from vc-cvs-state-heuristic.
176 (let ((checkout-time (vc-file-getprop file 'vc-checkout-time))
177 (lastmod (nth 5 (file-attributes file))))
178 (cond
179 ((equal checkout-time lastmod) 'up-to-date)
180 ((string= (vc-working-revision file) "0") 'added)
181 ((null checkout-time) 'unregistered)
182 (t 'edited))))
1fd3454a 183
a80a6b03
GM
184;; FIXME it would be better not to have the "remote" argument,
185;; but to distinguish the two output formats based on content.
186(defun vc-svn-after-dir-status (callback &optional remote)
c222c25f 187 (let ((state-map '((?A . added)
b2ee56c9 188 (?C . conflict)
b2ee56c9
SM
189 (?I . ignored)
190 (?M . edited)
e6c01f09 191 (?D . removed)
b2ee56c9
SM
192 (?R . removed)
193 (?? . unregistered)
194 ;; This is what vc-svn-parse-status does.
195 (?~ . edited)))
5dd4f3f7
GM
196 (re (if remote "^\\(.\\)\\(.\\).....? \\([ *]\\) +\\(?:[-0-9]+\\)? \\(.*\\)$"
197 ;; Subexp 3 is a dummy in this case, so the numbers match.
198 "^\\(.\\)\\(.\\)...\\(.\\) \\(.*\\)$"))
c222c25f
DN
199 result)
200 (goto-char (point-min))
a80a6b03 201 (while (re-search-forward re nil t)
c222c25f 202 (let ((state (cdr (assq (aref (match-string 1) 0) state-map)))
5dd4f3f7 203 (propstat (cdr (assq (aref (match-string 2) 0) state-map)))
614b85f8
VJ
204 (filename (if (memq system-type '(windows-nt ms-dos))
205 (replace-regexp-in-string "\\\\" "/" (match-string 4))
206 (match-string 4))))
dbfb414e
GM
207 (and (memq propstat '(conflict edited))
208 (not (eq state 'conflict)) ; conflict always wins
209 (setq state propstat))
5dd4f3f7 210 (and remote (string-equal (match-string 3) "*")
a80a6b03
GM
211 ;; FIXME are there other possible combinations?
212 (cond ((eq state 'edited) (setq state 'needs-merge))
213 ((not state) (setq state 'needs-update))))
524a655d 214 (when (and state (not (string= "." filename)))
1b3f2d4e 215 (setq result (cons (list filename state) result)))))
b2ee56c9 216 (funcall callback result)))
c222c25f 217
e658d75c
GM
218;; -dir-status called from vc-dir, which loads vc, which loads vc-dispatcher.
219(declare-function vc-exec-after "vc-dispatcher" (code))
220
c1b51374 221(defun vc-svn-dir-status (dir callback)
c222c25f
DN
222 "Run 'svn status' for DIR and update BUFFER via CALLBACK.
223CALLBACK is called as (CALLBACK RESULT BUFFER), where
224RESULT is a list of conses (FILE . STATE) for directory DIR."
a80a6b03 225 ;; FIXME should this rather be all the files in dir?
1826af5e
DN
226 ;; FIXME: the vc-stay-local-p logic below is disabled, it ends up
227 ;; calling synchronously (vc-svn-registered DIR) => calling svn status -v DIR
228 ;; which is VERY SLOW for big trees and it makes emacs
229 ;; completely unresponsive during that time.
77bf3f54 230 (let* ((local (and nil (vc-stay-local-p dir 'SVN)))
1826af5e 231 (remote (or t (not local) (eq local 'only-file))))
a80a6b03
GM
232 (vc-svn-command (current-buffer) 'async nil "status"
233 (if remote "-u"))
9c750eba
SM
234 (vc-run-delayed
235 (vc-svn-after-dir-status callback remote))))
f8e89f19 236
be94d713 237(defun vc-svn-dir-status-files (_dir files _default-state callback)
847fb889 238 (apply 'vc-svn-command (current-buffer) 'async nil "status" files)
9c750eba
SM
239 (vc-run-delayed
240 (vc-svn-after-dir-status callback)))
847fb889 241
be94d713 242(defun vc-svn-dir-extra-headers (_dir)
2ec0d864 243 "Generate extra status headers for a Subversion working copy."
04473f67
MA
244 (let (process-file-side-effects)
245 (vc-svn-command "*vc*" 0 nil "info"))
2ec0d864 246 (let ((repo
def61be2 247 (save-excursion
2ec0d864
ER
248 (and (progn
249 (set-buffer "*vc*")
250 (goto-char (point-min))
251 (re-search-forward "Repository Root: *\\(.*\\)" nil t))
252 (match-string 1)))))
253 (concat
254 (cond (repo
255 (concat
256 (propertize "Repository : " 'face 'font-lock-type-face)
257 (propertize repo 'face 'font-lock-variable-name-face)))
258 (t "")))))
259
ac3f4c6f
ER
260(defun vc-svn-working-revision (file)
261 "SVN-specific version of `vc-working-revision'."
1fd3454a
SM
262 ;; There is no need to consult RCS headers under SVN, because we
263 ;; get the workfile version for free when we recognize that a file
264 ;; is registered in SVN.
265 (vc-svn-registered file)
ac3f4c6f 266 (vc-file-getprop file 'vc-working-revision))
1fd3454a 267
5cc7cb96
SM
268;; vc-svn-mode-line-string doesn't exist because the default implementation
269;; works just fine.
270
9c750eba 271(defun vc-svn-previous-revision (_file rev)
cbbd2cd3
TTN
272 (let ((newrev (1- (string-to-number rev))))
273 (when (< 0 newrev)
274 (number-to-string newrev))))
275
5b5afd50 276(defun vc-svn-next-revision (file rev)
cbbd2cd3 277 (let ((newrev (1+ (string-to-number rev))))
5b5afd50 278 ;; The "working revision" is an uneasy conceptual fit under Subversion;
cbbd2cd3
TTN
279 ;; we use it as the upper bound until a better idea comes along. If the
280 ;; workfile version W coincides with the tree's latest revision R, then
281 ;; this check prevents a "no such revision: R+1" error. Otherwise, it
282 ;; inhibits showing of W+1 through R, which could be considered anywhere
283 ;; from gracious to impolite.
ac3f4c6f 284 (unless (< (string-to-number (vc-file-getprop file 'vc-working-revision))
cbbd2cd3
TTN
285 newrev)
286 (number-to-string newrev))))
287
1fd3454a
SM
288
289;;;
290;;; State-changing functions
291;;;
292
8cdd17b4
ER
293(defun vc-svn-create-repo ()
294 "Create a new SVN repository."
2888a97e 295 (vc-do-command "*vc*" 0 "svnadmin" '("create" "SVN"))
14afa541
GM
296 ;; Expand default-directory because svn gets confused by eg
297 ;; file://~/path/to/file. (Bug#15446).
9a4de110 298 (vc-svn-command "*vc*" 0 "." "checkout"
14afa541 299 (concat "file://" (expand-file-name default-directory) "SVN")))
8cdd17b4 300
e658d75c
GM
301(autoload 'vc-switches "vc")
302
9c750eba 303(defun vc-svn-register (files &optional _rev _comment)
8cdd17b4
ER
304 "Register FILES into the SVN version-control system.
305The COMMENT argument is ignored This does an add but not a commit.
c8d6b4bc
GM
306Passes either `vc-svn-register-switches' or `vc-register-switches'
307to the SVN command."
8cdd17b4 308 (apply 'vc-svn-command nil 0 files "add" (vc-switches 'SVN 'register)))
1fd3454a 309
b5a53795
KP
310(defun vc-svn-root (file)
311 (vc-find-root file vc-svn-admin-directory))
1fd3454a 312
b5a53795
KP
313(defalias 'vc-svn-responsible-p 'vc-svn-root)
314
315(defalias 'vc-svn-could-register 'vc-svn-root
1fd3454a
SM
316 "Return non-nil if FILE could be registered in SVN.
317This is only possible if SVN is responsible for FILE's directory.")
318
9c750eba 319(defun vc-svn-checkin (files rev comment &optional _extra-args-ignored)
1fd3454a 320 "SVN-specific version of `vc-backend-checkin'."
4ced8551 321 (if rev (error "Committing to a specific revision is unsupported in SVN"))
5129f10c 322 (let ((status (apply
8cdd17b4 323 'vc-svn-command nil 1 files "ci"
5129f10c 324 (nconc (list "-m" comment) (vc-switches 'SVN 'checkin)))))
1fd3454a
SM
325 (set-buffer "*vc*")
326 (goto-char (point-min))
327 (unless (equal status 0)
328 ;; Check checkin problem.
329 (cond
fd140743 330 ((search-forward "Transaction is out of date" nil t)
8cdd17b4
ER
331 (mapc (lambda (file) (vc-file-setprop file 'vc-state 'needs-merge))
332 files)
1fd3454a
SM
333 (error (substitute-command-keys
334 (concat "Up-to-date check failed: "
335 "type \\[vc-next-action] to merge in changes"))))
336 (t
337 (pop-to-buffer (current-buffer))
338 (goto-char (point-min))
339 (shrink-window-if-larger-than-buffer)
340 (error "Check-in failed"))))
341 ;; Update file properties
342 ;; (vc-file-setprop
ac3f4c6f 343 ;; file 'vc-working-revision
1fd3454a
SM
344 ;; (vc-parse-buffer "^\\(new\\|initial\\) revision: \\([0-9.]+\\)" 2))
345 ))
346
ac3f4c6f 347(defun vc-svn-find-revision (file rev buffer)
8cdd17b4 348 "SVN-specific retrieval of a specified version into a buffer."
04473f67
MA
349 (let (process-file-side-effects)
350 (apply 'vc-svn-command
351 buffer 0 file
352 "cat"
353 (and rev (not (string= rev ""))
354 (concat "-r" rev))
355 (vc-switches 'SVN 'checkout))))
1fd3454a 356
be94d713 357(defun vc-svn-ignore (file &optional _directory _remove)
ab419665 358 "Ignore FILE under Subversion.
63191d9f 359FILE is a file wildcard, relative to the root directory of DIRECTORY."
5c09de04 360 (vc-svn-command t 0 file "propedit" "svn:ignore"))
7aa7fff0 361
9c750eba 362(defun vc-svn-ignore-completion-table (_file)
ab419665
XF
363 "Return the list of ignored files."
364 )
365
34ca0f4c
XF
366(defun vc-svn-find-admin-dir (file)
367 "Return the administrative directory of FILE."
368 (expand-file-name vc-svn-admin-directory (vc-svn-root file)))
369
1fd3454a
SM
370(defun vc-svn-checkout (file &optional editable rev)
371 (message "Checking out %s..." file)
372 (with-current-buffer (or (get-file-buffer file) (current-buffer))
a749e19d 373 (vc-svn-update file editable rev (vc-switches 'SVN 'checkout)))
77bf3f54 374 (vc-mode-line file 'SVN)
1fd3454a
SM
375 (message "Checking out %s...done" file))
376
9c750eba 377(defun vc-svn-update (file _editable rev switches)
1fd3454a 378 (if (and (file-exists-p file) (not rev))
fc2fb30c
SM
379 ;; If no revision was specified, there's nothing to do.
380 nil
1fd3454a 381 ;; Check out a particular version (or recreate the file).
ac3f4c6f 382 (vc-file-setprop file 'vc-working-revision nil)
1fd3454a 383 (apply 'vc-svn-command nil 0 file
1fd3454a 384 "update"
5cc7cb96
SM
385 (cond
386 ((null rev) "-rBASE")
387 ((or (eq rev t) (equal rev "")) nil)
388 (t (concat "-r" rev)))
1fd3454a
SM
389 switches)))
390
3bdc13e4
SM
391(defun vc-svn-delete-file (file)
392 (vc-svn-command nil 0 file "remove"))
393
2766aaaf
SM
394(defun vc-svn-rename-file (old new)
395 (vc-svn-command nil 0 new "move" (file-relative-name old)))
396
1fd3454a
SM
397(defun vc-svn-revert (file &optional contents-done)
398 "Revert FILE to the version it was based on."
399 (unless contents-done
fc2fb30c 400 (vc-svn-command nil 0 file "revert")))
1fd3454a
SM
401
402(defun vc-svn-merge (file first-version &optional second-version)
403 "Merge changes into current working copy of FILE.
404The changes are between FIRST-VERSION and SECOND-VERSION."
405 (vc-svn-command nil 0 file
c217cb04 406 "merge"
02610d0e 407 "-r" (if second-version
c217cb04
SM
408 (concat first-version ":" second-version)
409 first-version))
1fd3454a
SM
410 (vc-file-setprop file 'vc-state 'edited)
411 (with-current-buffer (get-buffer "*vc*")
412 (goto-char (point-min))
c217cb04
SM
413 (if (looking-at "C ")
414 1 ; signal conflict
1fd3454a
SM
415 0))) ; signal success
416
417(defun vc-svn-merge-news (file)
418 "Merge in any new changes made to FILE."
419 (message "Merging changes into %s..." file)
ac3f4c6f 420 ;; (vc-file-setprop file 'vc-working-revision nil)
1fd3454a 421 (vc-file-setprop file 'vc-checkout-time 0)
9a4de110 422 (vc-svn-command nil 0 file "update")
1fd3454a
SM
423 ;; Analyze the merge result reported by SVN, and set
424 ;; file properties accordingly.
425 (with-current-buffer (get-buffer "*vc*")
426 (goto-char (point-min))
5b5afd50 427 ;; get new working revision
1fd3454a 428 (if (re-search-forward
1468d754 429 "^\\(Updated to\\|At\\) revision \\([0-9]+\\)" nil t)
ac3f4c6f
ER
430 (vc-file-setprop file 'vc-working-revision (match-string 2))
431 (vc-file-setprop file 'vc-working-revision nil))
1fd3454a 432 ;; get file status
1468d754 433 (goto-char (point-min))
1fd3454a 434 (prog1
1468d754 435 (if (looking-at "At revision")
1fd3454a
SM
436 0 ;; there were no news; indicate success
437 (if (re-search-forward
459b1fe4
SM
438 ;; Newer SVN clients have 3 columns of chars (one for the
439 ;; file's contents, then second for its properties, and the
440 ;; third for lock-grabbing info), before the 2 spaces.
441 ;; We also used to match the filename in column 0 without any
442 ;; meta-info before it, but I believe this can never happen.
443 (concat "^\\(\\([ACGDU]\\)\\(.[B ]\\)? \\)"
c2d07557 444 (regexp-quote (file-relative-name file)))
1fd3454a
SM
445 nil t)
446 (cond
447 ;; Merge successful, we are in sync with repository now
459b1fe4 448 ((string= (match-string 2) "U")
1fd3454a
SM
449 (vc-file-setprop file 'vc-state 'up-to-date)
450 (vc-file-setprop file 'vc-checkout-time
451 (nth 5 (file-attributes file)))
452 0);; indicate success to the caller
453 ;; Merge successful, but our own changes are still in the file
459b1fe4 454 ((string= (match-string 2) "G")
1fd3454a
SM
455 (vc-file-setprop file 'vc-state 'edited)
456 0);; indicate success to the caller
457 ;; Conflicts detected!
458 (t
459 (vc-file-setprop file 'vc-state 'edited)
460 1);; signal the error to the caller
461 )
462 (pop-to-buffer "*vc*")
463 (error "Couldn't analyze svn update result")))
464 (message "Merging changes into %s...done" file))))
465
9c750eba 466(defun vc-svn-modify-change-comment (_files rev comment)
42a0a135
ER
467 "Modify the change comments for a specified REV.
468You must have ssh access to the repository host, and the directory Emacs
e09deae9 469uses locally for temp files must also be writable by you on that host.
fd064451
DN
470This is only supported if the repository access method is either file://
471or svn+ssh://."
472 (let (tempfile host remotefile directory fileurl-p)
42a0a135 473 (with-temp-buffer
9a4de110 474 (vc-svn-command (current-buffer) 0 nil "info")
fd064451
DN
475 (goto-char (point-min))
476 (unless (re-search-forward "Repository Root: \\(file://\\(/.*\\)\\)\\|\\(svn\\+ssh://\\([^/]+\\)\\(/.*\\)\\)" nil t)
477 (error "Repository information is unavailable"))
478 (if (match-string 1)
479 (progn
480 (setq fileurl-p t)
481 (setq directory (match-string 2)))
482 (setq host (match-string 4))
483 (setq directory (match-string 5))
484 (setq remotefile (concat host ":" tempfile))))
485 (with-temp-file (setq tempfile (make-temp-file user-mail-address))
486 (insert comment))
487 (if fileurl-p
488 ;; Repository Root is a local file.
489 (progn
490 (unless (vc-do-command
2888a97e 491 "*vc*" 0 "svnadmin" nil
def61be2 492 "setlog" "--bypass-hooks" directory
fd064451
DN
493 "-r" rev (format "%s" tempfile))
494 (error "Log edit failed"))
495 (delete-file tempfile))
496
497 ;; Remote repository, using svn+ssh.
2888a97e 498 (unless (vc-do-command "*vc*" 0 "scp" nil "-q" tempfile remotefile)
fd064451
DN
499 (error "Copy of comment to %s failed" remotefile))
500 (unless (vc-do-command
2888a97e 501 "*vc*" 0 "ssh" nil "-q" host
fd064451
DN
502 (format "svnadmin setlog --bypass-hooks %s -r %s %s; rm %s"
503 directory rev tempfile tempfile))
504 (error "Log edit failed")))))
1fd3454a
SM
505
506;;;
507;;; History functions
508;;;
509
def61be2
JB
510(defvar log-view-per-file-logs)
511
6653c6b7
DN
512(define-derived-mode vc-svn-log-view-mode log-view-mode "SVN-Log-View"
513 (require 'add-log)
514 (set (make-local-variable 'log-view-per-file-logs) nil))
515
e658d75c
GM
516(autoload 'vc-setup-buffer "vc-dispatcher")
517
9c750eba 518(defun vc-svn-print-log (files buffer &optional _shortlog start-revision limit)
bb7cdf58
GM
519 "Print commit log associated with FILES into specified BUFFER.
520SHORTLOG is ignored.
521If START-REVISION is non-nil, it is the newest revision to show.
522If LIMIT is non-nil, show no more than this many entries."
1fd3454a 523 (save-current-buffer
b349012b 524 (vc-setup-buffer buffer)
1fd3454a
SM
525 (let ((inhibit-read-only t))
526 (goto-char (point-min))
13b56025
ER
527 (if files
528 (dolist (file files)
529 (insert "Working file: " file "\n")
6616006b
DN
530 (apply
531 'vc-svn-command
13b56025
ER
532 buffer
533 'async
77bf3f54 534 ;; (if (and (= (length files) 1) (vc-stay-local-p file 'SVN)) 'async 0)
13b56025
ER
535 (list file)
536 "log"
662c5698
DN
537 (append
538 (list
539 (if start-revision
90b4237a 540 (format "-r%s:1" start-revision)
662c5698
DN
541 ;; By default Subversion only shows the log up to the
542 ;; working revision, whereas we also want the log of the
543 ;; subsequent commits. At least that's what the
544 ;; vc-cvs.el code does.
545 "-rHEAD:0"))
1db3226b 546 (when limit (list "--limit" (format "%s" limit))))))
13b56025 547 ;; Dump log for the entire directory.
662c5698
DN
548 (apply 'vc-svn-command buffer 0 nil "log"
549 (append
550 (list
551 (if start-revision (format "-r%s" start-revision) "-rHEAD:0"))
1db3226b 552 (when limit (list "--limit" (format "%s" limit)))))))))
8cdd17b4 553
8cdd17b4 554(defun vc-svn-diff (files &optional oldvers newvers buffer)
5b5afd50 555 "Get a difference report using SVN between two revisions of fileset FILES."
22b5692c
NR
556 (and oldvers
557 (not newvers)
558 files
559 (catch 'no
560 (dolist (f files)
561 (or (equal oldvers (vc-working-revision f))
562 (throw 'no nil)))
563 t)
564 ;; Use nil rather than the current revision because svn handles
565 ;; it better (i.e. locally). Note that if _any_ of the files
566 ;; has a different revision, we fetch the lot, which is
567 ;; obviously sub-optimal.
568 (setq oldvers nil))
8cdd17b4 569 (let* ((switches
f9d1f3be
SM
570 (if vc-svn-diff-switches
571 (vc-switches 'SVN 'diff)
ed68f651 572 (list (concat "--diff-cmd=" diff-command) "-x"
9751169a 573 (mapconcat 'identity (vc-switches nil 'diff) " "))))
2d4e93b9 574 (async (and (not vc-disable-async-diff)
77bf3f54 575 (vc-stay-local-p files 'SVN)
fe1919ab 576 (or oldvers newvers)))) ; Svn diffs those locally.
b349012b 577 (apply 'vc-svn-command buffer
2766aaaf 578 (if async 'async 0)
8cdd17b4 579 files "diff"
2766aaaf 580 (append
f9d1f3be 581 switches
2766aaaf
SM
582 (when oldvers
583 (list "-r" (if newvers (concat oldvers ":" newvers)
584 oldvers)))))
fd140743
SM
585 (if async 1 ; async diff => pessimistic assumption
586 ;; For some reason `svn diff' does not return a useful
587 ;; status w.r.t whether the diff was empty or not.
8cdd17b4 588 (buffer-size (get-buffer buffer)))))
1fd3454a 589
1fd3454a 590;;;
370fded4 591;;; Tag system
1fd3454a
SM
592;;;
593
370fded4 594(defun vc-svn-create-tag (dir name branchp)
5b5afd50 595 "Assign to DIR's current revision a given NAME.
1fd3454a 596If BRANCHP is non-nil, the name is created as a branch (and the current
5cc7cb96
SM
597workspace is immediately moved to that new branch).
598NAME is assumed to be a URL."
599 (vc-svn-command nil 0 dir "copy" name)
370fded4 600 (when branchp (vc-svn-retrieve-tag dir name nil)))
1fd3454a 601
aa30fa6f 602(defun vc-svn-retrieve-tag (dir name _update)
370fded4
ER
603 "Retrieve a tag at and below DIR.
604NAME is the name of the tag; if it is empty, do a `svn update'.
5cc7cb96
SM
605If UPDATE is non-nil, then update (resynch) any affected buffers.
606NAME is assumed to be a URL."
607 (vc-svn-command nil 0 dir "switch" name)
608 ;; FIXME: parse the output and obey `update'.
609 )
1fd3454a
SM
610
611;;;
612;;; Miscellaneous
613;;;
614
615;; Subversion makes backups for us, so don't bother.
77bf3f54
DN
616;; (defun vc-svn-make-version-backups-p (file)
617;; "Return non-nil if version backups should be made for FILE."
618;; (vc-stay-local-p file 'SVN))
1fd3454a
SM
619
620(defun vc-svn-check-headers ()
621 "Check if the current file has any headers in it."
622 (save-excursion
623 (goto-char (point-min))
624 (re-search-forward "\\$[A-Za-z\300-\326\330-\366\370-\377]+\
625\\(: [\t -#%-\176\240-\377]*\\)?\\$" nil t)))
626
627
628;;;
629;;; Internal functions
630;;;
631
8cdd17b4 632(defun vc-svn-command (buffer okstatus file-or-list &rest flags)
1fd3454a
SM
633 "A wrapper around `vc-do-command' for use in vc-svn.el.
634The difference to vc-do-command is that this function always invokes `svn',
31db8c31 635and that it passes `vc-svn-global-switches' to it before FLAGS."
9a4de110 636 (apply 'vc-do-command (or buffer "*vc*") okstatus vc-svn-program file-or-list
31db8c31
GM
637 (if (stringp vc-svn-global-switches)
638 (cons vc-svn-global-switches flags)
639 (append vc-svn-global-switches flags))))
1fd3454a 640
7ffc77d3
SM
641(defun vc-svn-repository-hostname (dirname)
642 (with-temp-buffer
b5a53795
KP
643 (let (process-file-side-effects)
644 (vc-svn-command t t dirname "info" "--xml"))
7ffc77d3 645 (goto-char (point-min))
b5a53795 646 (when (re-search-forward "<url>\\(.*\\)</url>" nil t)
17a5a301
SM
647 ;; This is not a hostname but a URL. This may actually be considered
648 ;; as a feature since it allows vc-svn-stay-local to specify different
649 ;; behavior for different modules on the same server.
650 (match-string 1))))
1fd3454a 651
1c67a814
SM
652(defun vc-svn-resolve-when-done ()
653 "Call \"svn resolved\" if the conflict markers have been removed."
654 (save-excursion
655 (goto-char (point-min))
54648b5c
DN
656 (unless (re-search-forward "^<<<<<<< " nil t)
657 (vc-svn-command nil 0 buffer-file-name "resolved")
658 ;; Remove the hook so that it is not called multiple times.
659 (remove-hook 'after-save-hook 'vc-svn-resolve-when-done t))))
1c67a814
SM
660
661;; Inspired by vc-arch-find-file-hook.
662(defun vc-svn-find-file-hook ()
663 (when (eq ?C (vc-file-getprop buffer-file-name 'vc-svn-status))
664 ;; If the file is marked as "conflicted", then we should try and call
665 ;; "svn resolved" when applicable.
666 (if (save-excursion
667 (goto-char (point-min))
668 (re-search-forward "^<<<<<<< " nil t))
669 ;; There are conflict markers.
670 (progn
28e4e2b4 671 (smerge-start-session)
1c67a814
SM
672 (add-hook 'after-save-hook 'vc-svn-resolve-when-done nil t))
673 ;; There are no conflict markers. This is problematic: maybe it means
674 ;; the conflict has been resolved and we should immediately call "svn
675 ;; resolved", or it means that the file's type does not allow Svn to
676 ;; use conflict markers in which case we don't really know what to do.
677 ;; So let's just punt for now.
678 nil)
679 (message "There are unresolved conflicts in this file")))
680
bc8c1bb4 681(defun vc-svn-parse-status (&optional filename)
1fd3454a 682 "Parse output of \"svn status\" command in the current buffer.
a6ae021f
GM
683Set file properties accordingly. If FILENAME is non-nil, return its status."
684 (let (multifile file status propstat)
1fd3454a
SM
685 (goto-char (point-min))
686 (while (re-search-forward
484c1b1f 687 ;; Ignore the files with status X.
245cacf1 688 "^\\(?:\\?\\|[ ACDGIMR!~][ MC][ L][ +][ S]..\\([ *]\\) +\\([-0-9]+\\) +\\([0-9?]+\\) +\\([^ ]+\\)\\) +" nil t)
c45b3be3
SM
689 ;; If the username contains spaces, the output format is ambiguous,
690 ;; so don't trust the output's filename unless we have to.
a6ae021f 691 (setq file (or (unless multifile filename)
c45b3be3 692 (expand-file-name
a6ae021f
GM
693 (buffer-substring (point) (line-end-position))))
694 ;; If we are parsing the result of running status on a directory,
695 ;; there could be multiple files in the output.
696 ;; We assume that filename, if supplied, applies to the first
697 ;; listed file (ie, the directory). Bug#15322.
698 multifile t
699 status (char-after (line-beginning-position))
5dd4f3f7
GM
700 ;; Status of the item's properties ([ MC]).
701 propstat (char-after (1+ (line-beginning-position))))
484c1b1f 702 (if (eq status ??)
4f07b9f2 703 (vc-file-setprop file 'vc-state 'unregistered)
fd140743
SM
704 ;; Use the last-modified revision, so that searching in vc-print-log
705 ;; output works.
4f07b9f2 706 (vc-file-setprop file 'vc-working-revision (match-string 3))
1c67a814 707 ;; Remember Svn's own status.
4f07b9f2
ER
708 (vc-file-setprop file 'vc-svn-status status)
709 (vc-file-setprop
1fd3454a
SM
710 file 'vc-state
711 (cond
5dd4f3f7 712 ((and (eq status ?\ ) (eq propstat ?\ ))
1fd3454a 713 (if (eq (char-after (match-beginning 1)) ?*)
3702367b 714 'needs-update
4f07b9f2 715 (vc-file-setprop file 'vc-checkout-time
1fd3454a
SM
716 (nth 5 (file-attributes file)))
717 'up-to-date))
718 ((eq status ?A)
fd140743 719 ;; If the file was actually copied, (match-string 2) is "-".
4f07b9f2
ER
720 (vc-file-setprop file 'vc-working-revision "0")
721 (vc-file-setprop file 'vc-checkout-time 0)
484c1b1f 722 'added)
5dd4f3f7
GM
723 ;; Conflict in contents or properties.
724 ((or (eq status ?C) (eq propstat ?C))
7fbb4797 725 (vc-file-setprop file 'vc-state 'conflict))
5dd4f3f7
GM
726 ;; Modified contents or properties.
727 ((or (eq status ?M) (eq propstat ?M))
1fd3454a
SM
728 (if (eq (char-after (match-beginning 1)) ?*)
729 'needs-merge
730 'edited))
722f037f 731 ((eq status ?I)
4f07b9f2 732 (vc-file-setprop file 'vc-state 'ignored))
e6c01f09 733 ((memq status '(?D ?R))
4f07b9f2 734 (vc-file-setprop file 'vc-state 'removed))
bc8c1bb4 735 (t 'edited)))))
245cacf1 736 (when filename (vc-file-getprop filename 'vc-state))))
1fd3454a 737
1fd3454a
SM
738(defun vc-svn-valid-symbolic-tag-name-p (tag)
739 "Return non-nil if TAG is a valid symbolic tag name."
740 ;; According to the SVN manual, a valid symbolic tag must start with
741 ;; an uppercase or lowercase letter and can contain uppercase and
742 ;; lowercase letters, digits, `-', and `_'.
743 (and (string-match "^[a-zA-Z]" tag)
744 (not (string-match "[^a-z0-9A-Z-_]" tag))))
745
5b5afd50
ER
746(defun vc-svn-valid-revision-number-p (tag)
747 "Return non-nil if TAG is a valid revision number."
1fd3454a
SM
748 (and (string-match "^[0-9]" tag)
749 (not (string-match "[^0-9]" tag))))
750
17a5a301
SM
751;; Support for `svn annotate'
752
753(defun vc-svn-annotate-command (file buf &optional rev)
837b0e99 754 (vc-svn-command buf 'async file "annotate" (if rev (concat "-r" rev))))
17a5a301
SM
755
756(defun vc-svn-annotate-time-of-rev (rev)
da6062e6 757 ;; Arbitrarily assume 10 commits per day.
17a5a301
SM
758 (/ (string-to-number rev) 10.0))
759
e53ac718
DN
760(defvar vc-annotate-parent-rev)
761
17a5a301
SM
762(defun vc-svn-annotate-current-time ()
763 (vc-svn-annotate-time-of-rev vc-annotate-parent-rev))
764
765(defconst vc-svn-annotate-re "[ \t]*\\([0-9]+\\)[ \t]+[^\t ]+ ")
766
767(defun vc-svn-annotate-time ()
768 (when (looking-at vc-svn-annotate-re)
769 (goto-char (match-end 0))
770 (vc-svn-annotate-time-of-rev (match-string 1))))
771
772(defun vc-svn-annotate-extract-revision-at-line ()
773 (save-excursion
774 (beginning-of-line)
775 (if (looking-at vc-svn-annotate-re) (match-string 1))))
776
8228a275
MH
777(defun vc-svn-revision-table (files)
778 (let ((vc-svn-revisions '()))
779 (with-current-buffer "*vc*"
780 (vc-svn-command nil 0 files "log" "-q")
781 (goto-char (point-min))
782 (forward-line)
783 (let ((start (point-min))
784 (loglines (buffer-substring-no-properties (point-min)
785 (point-max))))
786 (while (string-match "^r\\([0-9]+\\) " loglines)
787 (push (match-string 1 loglines) vc-svn-revisions)
788 (setq start (+ start (match-end 0)))
789 (setq loglines (buffer-substring-no-properties start (point-max)))))
790 vc-svn-revisions)))
791
1fd3454a
SM
792(provide 'vc-svn)
793
794;;; vc-svn.el ends here