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